/*!@file \brief IR Memory Implementation \details Intermediary memory management \author Jordan Lavatai \date Aug 2016 ----------------------------------------------------------------------------*/ #include #include #include //uint8_t as a char #include //u32_cpy #include //uint64_t #include //memset #include //u8_* functions #include "apc.h" extern int name_u8_cpy(struct name*, struct name*); extern int name_u8_cmp(struct name*, struct name*); extern int name_u8_set(struct name*, ucs4_t); int ir_init(void); struct cdat* alloc_cdat(void); struct odat* alloc_odat(void); void alloc_vdat(void); struct link* alloc_link(void); struct ref* alloc_ref(void); struct set* alloc_set(void); struct cdat* curr_cdat(void); struct odat* curr_odat(void); struct vdat* curr_vdat(void); struct map* curr_map(void); struct set* curr_cdat_set(void); struct set* curr_set(void); struct ref* curr_ref(void); struct model* curr_model(void); /* ir.c */ void inc_posts(void); void push_cdat(struct name*); void pop_cdat(void); void insert_refid(int); void insert_link_name(struct name*); void insert_link_namelist(struct name*); void insert_ss_name(struct name*); void insert_ss_namelist(struct name*); void insert_mlink(struct name*, int); void insert_vlink(struct name*, int); void insert_ref(struct odat*, int); void alloc_vdat(void); void insert_vdat(void); void insert_map(struct name*, int, int, int, int, uint8_t*); void insert_framesheet(struct name*, int, int, int, int, uint8_t*); //type safety handled by macro expansion (do not call these directly from code, make dependent macros for access to these) #define CHUNKS_LEN(STACK) ((STACK).csp - (STACK).chunks) #define CURRENT_CHUNK(STACK) ((STACK).chunks[CHUNKS_LEN(STACK) - 1]) #define CHUNKS_FULL(STACK) ( (STACK).csp >= \ (STACK).chunks + MAX_CHUNKS * (STACK).chunk_size) #define CURRENT_DSP(STACK,TYPE) ((TYPE*) ((STACK).dsp[CHUNKS_LEN(STACK) - 1])) #define DATA_FULL(STACK,TYPE) ((void*) CURRENT_DSP(STACK,TYPE) >= \ (CURRENT_CHUNK(STACK) + (STACK).chunk_size)) #define CSP_PUSH(STACK) (*(++(STACK).csp) = malloc((STACK).chunk_size)) #define CURRENT_DATP(STACK,TYPE) (((TYPE**)(STACK).dsp)[CHUNKS_LEN(STACK) - 1]) #define PREVIOUS_DATP(STACK,TYPE) (((TYPE**)(STACK).dsp)[CHUNKS_LEN(STACK) - 2]) #define ALLOC_DAT(STACK,TYPE) (++CURRENT_DATP(STACK,TYPE)) #define INIT_STACK(STACK,TYPE) \ { int i; \ (STACK).chunk_size = PAGES_PER_CHUNK * pagesize; \ (STACK).max_dats = (STACK).chunk_size / sizeof (TYPE); \ CSP_PUSH(STACK); \ for( i = 0; i < MAX_CHUNKS; i++){ \ (STACK).dsp[i] += pagesize; \ } \ } //Stack-specific macros (called directly from code (safety enforcement) #define INIT_ODAT() (INIT_STACK(ocs, struct odat)) #define CURRENT_ODAT() (CURRENT_DATP(ocs,struct odat)) #define ODAT_FULL() (DATA_FULL(ocs,struct odat)) #define ODAT_ALLOC() (ALLOC_DAT(ocs,struct odat)) #define OCS_FULL() (CHUNKS_FULL(ocs)) #define INIT_VDAT() (INIT_STACK(vcs, struct vdat)) #define CURRENT_VDAT() (CURRENT_DATP(vcs,struct vdat)) #define VDAT_FULL() (DATA_FULL(vcs,struct vdat)) #define VDAT_ALLOC() (ALLOC_DAT(vcs,struct vdat)) #define VCS_FULL() (CHUNKS_FULL(vcs)) #define INIT_CDAT() (INIT_STACK(ccs, struct cdat)) #define CURRENT_CDAT() (CURRENT_DATP(ccs,struct cdat)) #define CDAT_FULL() (DATA_FULL(ccs, struct cdat)) #define CDAT_ALLOC() (ALLOC_DAT(ccs, struct cdat)) #define CCS_FULL() (CHUNKS_FULL(ccs)) #define INIT_SET() (INIT_STACK(scs, struct set)) #define CURRENT_SET() (CURRENT_DATP(scs, struct set)) #define SET_FULL() (DATA_FULL(scs, struct set)) #define SET_ALLOC() (ALLOC_DAT(scs, struct set)) #define SCS_FULL() (CHUNKS_FULL(scs)) #define INIT_LINK() (INIT_STACK(lcs, struct link)) #define CURRENT_LINK() (CURRENT_DATP(lcs,struct link)) #define LDAT_FULL() (DATA_FULL(lcs, struct link)) #define LDAT_ALLOC() (ALLOC_DAT(lcs, struct link)) #define LCS_FULL() (CHUNKS_FULL(lcs)) #define INIT_POST() (INIT_STACK(rcs, struct ref)) #define CURRENT_POST() (CURRENT_DATP(pcs,struct ref)) #define POST_FULL() (DATA_FULL(pcs,struct ref)) #define POST_ALLOC() (ALLOC_DAT(pcs,struct ref)) #define PCS_FULL() (CHUNKS_FULL(pcs)) #define INIT_REF() (INIT_STACK(rcs, struct ref)) #define CURRENT_REF() (CURRENT_DATP(rcs,struct ref)) #define PREVIOUS_REF() (PREVIOUS_DATP(rcs, struct ref)) #define REF_FULL() (DATA_FULL(rcs,struct ref)) #define REF_ALLOC() (ALLOC_DAT(rcs,struct ref)) #define RCS_FULL() (CHUNKS_FULL(rcs)) //Metadata #define CURRENT_MODEL() (CURRENT_VDAT()->model_list[CURRENT_VDAT()->num_models]) /* Cdats: A cdat is a class data structure. Cdats serve as the central */ /* data types of the IR. Cdats contain pointers to their subclasses so that the relationship between */ /* classes can be determined, but the subclasses are not represented inside */ /* of the cdat itself but rather in subsequent cdats in cdat_buf. We */ /* can determine the number of subclasses (the last index into cdat_buf */ /* that represents a subclass of some arbitrary cdat) each cdat has by */ /* incrementing num_classes during parse time. */ /* TODO: Should classes point to their parent class? */ /* TODO: Talk more about cdat set structure */ struct cdat { struct name name; int idx; int num_classes; int num_sets; struct cdat* class_list[MAX_CLASSES]; struct set* set_list[MAX_SETS]; }; struct set { int cdat_idx; int num_sets; struct set* set_list[MAX_SETS]; }; struct ref { struct ref* nextref; struct ref* lastref; struct odat* odatp; int refid; //0xFFFFFF->digit }; struct olink { int src_refid; }; struct vlink { int src_refid; struct name src_animname; struct name src_namelist[MAX_DEPTH]; }; struct mlink { int src_refid; struct name src_mapname; struct name src_namelist[MAX_DEPTH]; }; union link_t { struct vlink vlink; struct mlink mlink; struct olink olink; }; /* Links are a mechanism for designers to indicate in the grammar that a odat, vdat, or map is defined elsewhere and that the link should be replaced with the specified odat/vdat/map */ struct link { int type; //1 = olink, 2 = vlink, 3 = mlink union link_t link_t; int dest_refid; //if it exists struct odat* dest_odatp; }; struct map { struct name name; int height; int width; uint8_t filepath[FPATH_MAX]; }; /* Odats: Odats consist of the object data necessary for each object. Odats are sometimes referred to as archetypes at compile-time, in order to distinguish the difference from a runtime object and a compile-time object. TODO: Need more info about objects at runtime, to described the reasoning behind odat structure at compile-time*/ struct odat { struct name name; int refid; int ismap; int vdat_idx; struct link* linkp; struct vdat* vdatp; struct odat* parent_odatp; /* null if parent is a cdat */ struct ref* refp; /* pointer to it's ref on ref_list */ struct map map; /* only valid if odat ismap */ }; /* A framesheet is a grouping of animation frames in a single direction (N,W,S,E, etc.). Framesheets also hold the framesheet dimensions and the filepath to the png of the framesheet so the file can be opened and the png data can be extracted. */ struct framesheet { int width; int height; uint8_t filepath[FPATH_MAX]; int num_frames; }; /* A model is a collection of framesheets for every direction (N,W,S,E,NW,NE,SW,SE). Currently, users can only define framesheets in the APC grammar, which are inserted into the current model*/ struct model { struct name name; struct framesheet spritesheet[8]; //one for each }; /* Vdat: Vdats are the video data of each object. Vdats have a list of models for every animation that the vdats odat can do for that vdat. */ struct vdat { struct odat* creator; //pointer to odat that made this vdat int num_models; struct model model_list[MAX_MODELS]; }; /* An entry on the set_stack that describes the namelist and relevant information for the current set that is being processed in the parser. For each set name, there is a corresponding set/odat that is created when set names are encountered. */ struct set_frame { struct name namelist[MAX_DEPTH]; int num_names; struct set* setp; struct odat* odatp; } ; /* Stores the last defined set at every depth */ struct set_stack { struct set_frame set_frames[MAX_DEPTH]; int curr_depth; //used to get most recently created set/odat + to check for undefined parents of namelists }; //"type free" chunk stacking struct chunk_stack { void* chunks[MAX_CHUNKS]; void* *csp; //chunk stack pointer void* dsp[MAX_CHUNKS]; //dat stack pointer (per chunk) int chunk_size; //size of a chunk (including its forfeited page) int max_dats; //number of dats per chunk for this stack } ocs, vcs, ccs, rcs, lcs, pcs, scs; //odat, vdat, cdat, ref, link, post stacks /* The cdat_stack is a stack pointers to cdat pointers, the top of which is the cdat that is currently being parsed. Whenever a new cdat is recognized by the grammar (CLOPEN), a cdat is pushed onto the cdat_stack, and we refer to this cdat through the macro CURR_CDAT. By keeping a cdat_stack, we have access to the current cdat so that the elements and sets can populate themselves in the cdat accordingly. */ struct cdat* cdat_stack[MAX_CLASSES]; struct cdat** cdat_stackp; struct set_stack ss; struct name set_namelist[MAX_DEPTH]; int set_numnames = 0; struct name link_namelist[MAX_DEPTH]; int link_numnames = 0; int num_cdats = 0; int num_odats = 0; int num_vdats = 0; int num_sets = 0; int num_refs = 0; int ss_refid = 0x0FFFFFFF; /* system space for refids */ int num_posts = 0; int num_links = 0; int num_models = 0; long pagesize = 0; /* The initalization function of the IR. */ int ir_init() { struct name name; uint8_t root[4] = "root"; u8_stpncpy(name.name, root, 4); pagesize = sysconf(_SC_PAGESIZE); INIT_CDAT(); *cdat_stackp = CURRENT_CDAT(); name_u8_cpy(&(*cdat_stackp)->name, &name); INIT_ODAT(); INIT_VDAT(); VDAT_ALLOC(); //NULL vdat VDAT_ALLOC(); //First vdat req. because alloc_vdat happens after vdat is reduced INIT_SET(); INIT_LINK(); INIT_REF(); INIT_POST(); return 0; } void ir_quit() { int i; for(i = 0; i < CHUNKS_LEN(ccs) ; i++) { free(ccs.chunks[i]); } for(i = 0; i < CHUNKS_LEN(ocs); i++) { free(ocs.chunks[i]); } for(i = 0; i < CHUNKS_LEN(vcs) ; i++) { free(vcs.chunks[i]); } for(i = 0; i < CHUNKS_LEN(rcs); i++) { free(rcs.chunks[i]); } for(i = 0; i < CHUNKS_LEN(lcs); i++) { free(lcs.chunks[i]); } for(i = 0; i < CHUNKS_LEN(pcs); i++) { free(pcs.chunks[i]); } } struct cdat* alloc_cdat() { num_cdats++; if(CDAT_FULL()) { if(CCS_FULL()) { fprintf(stderr, "You have allocated to many (%d) cdats ", num_cdats); exit(EXIT_FAILURE); } else CSP_PUSH(ccs); } else CDAT_ALLOC(); return CURRENT_CDAT(); } //these should probably be inline struct odat* alloc_odat () { num_odats++; if(ODAT_FULL()) { if(!OCS_FULL()) { fprintf(stderr, "You have allocated to many (%d) odats ", num_odats); exit(EXIT_FAILURE); } else CSP_PUSH(ocs); } else ODAT_ALLOC(); return CURRENT_ODAT(); } void alloc_vdat () { num_vdats++; if(VDAT_FULL()) { if(!VCS_FULL()) { fprintf(stderr, "You have allocated to many (%d) vdats ", num_vdats); exit(EXIT_FAILURE); } else CSP_PUSH(vcs); } else VDAT_ALLOC(); } struct set* alloc_set () { num_sets++; if(SET_FULL()) { if(!SCS_FULL()) { fprintf(stderr, "You have allocated to many (%d) sets ", num_sets); exit(EXIT_FAILURE); } else CSP_PUSH(scs); } else SET_ALLOC(); return CURRENT_SET(); } struct link* alloc_link () { num_links++; if(LDAT_FULL()) { if(!LCS_FULL()) { fprintf(stderr, "You have allocated to many (%d) links ", num_links); exit(EXIT_FAILURE); } else CSP_PUSH(lcs); } else LDAT_ALLOC(); return CURRENT_LINK(); } struct ref* alloc_ref () { num_refs++; if(REF_FULL()) { if(!RCS_FULL()) { fprintf(stderr, "You have allocated to many (%d) refs ", num_refs); exit(EXIT_FAILURE); } else CSP_PUSH(rcs); } else REF_ALLOC(); if(num_refs % 16 == 0) { CURRENT_POST() = CURRENT_REF(); inc_posts(); } return CURRENT_REF(); } void inc_posts() { num_posts++; if(POST_FULL()) { if(!PCS_FULL()) { fprintf(stderr, "You have allocated to many (%d) refs ", num_posts); exit(EXIT_FAILURE); } else CSP_PUSH(pcs); } else POST_ALLOC(); } struct cdat* curr_cdat () { return (*cdat_stackp); } struct odat* curr_odat () { return CURRENT_ODAT(); } struct vdat* curr_vdat () { return CURRENT_VDAT(); } struct set* curr_cdat_set () { return CURRENT_SET(); } struct ref* curr_ref () { return CURRENT_REF(); } struct ref* prev_ref () { return PREVIOUS_REF(); } struct model* curr_model () { return &CURRENT_MODEL(); } /* IR.C*/ void push_cdat ( struct name* name ) { struct cdat* curr_cdatp; curr_cdatp = alloc_cdat(); name_u8_cpy(&curr_cdatp->name, name); curr_cdatp->idx = num_cdats; /* Set the cdat as a subclass of the previous cdat */ (*cdat_stackp)->class_list[(*cdat_stackp)->num_classes++] = curr_cdatp; /* Push the cdat onto the cdat_stack */ *++cdat_stackp = curr_cdatp; } void pop_cdat () { cdat_stackp--; } void insert_set_name ( struct name* name ) { //Push name onto current namelist, set the set_namelist. name_u8_cpy(&set_namelist[set_numnames++], name); } /* Called at the last name of a sets namelist. Inserts the set namelist onto the set_stack at the appropriate depth i.e. the number of names in the namelist. If each name in the namelist at every depth matches, nothing happens. For every name on the namelist that doesnt match what is currently on the set_stack, a new set/odat is created at the depth that it describes. E.g. a set name of A_B_C is a depth of 3 and is represented in the set_stack as A, A_B and A_B_C. If a new set namelist is defined, X_Y, the new set_stack will become X, X_Y. */ void insert_set_namelist ( struct name* name ) { int depth, nameidx, i; insert_set_name(name); for( depth = 0; depth < set_numnames ; depth++ ) { for (nameidx = 0; nameidx <= depth; nameidx++) { if( name_u8_cmp(&set_namelist[nameidx], &ss.set_frames[depth].namelist[nameidx]) != 0 ) { /* Populate the namelist of the set at the current depth */ for(i = 0; i <= depth; i++) name_u8_cpy(&ss.set_frames[depth].namelist[i], &set_namelist[i]); /* Alloc set and odat */ ss.set_frames[depth].odatp = alloc_odat(); ss.set_frames[depth].setp = alloc_set(); /* populate set/odat name and cdat_idx */ name_u8_cpy(&ss.set_frames[depth].odatp->name, &set_namelist[depth]); ss.set_frames[depth].setp->cdat_idx = ( *cdat_stackp)->idx; /* Insert allocated set and odat into their respective trees if there is a depth (they have parents) */ if(depth) { ss.set_frames[depth].odatp->parent_odatp = ss.set_frames[depth-1].odatp; if(ss.set_frames[depth-1].setp->num_sets < MAX_SETS) ss.set_frames[depth-1].setp->set_list[ss.set_frames[depth-1].setp->num_sets++] = ss.set_frames[depth].setp; else { printf("you have allocated too many sets in insert_namelist()\n"); //TODO: EXIT() } } else /* no parent set, so assign to cdat set_list */ { ss.set_frames[depth].odatp->parent_odatp = NULL; //no parent odat = NULL. if(curr_cdat_set()->num_sets < MAX_SETS) curr_cdat_set()->set_list[curr_cdat_set()->num_sets++] = ss.set_frames[depth].setp; else { printf("you have allocated too many sets in insert_namelist()\n"); //TODO: EXIT() } } ss.set_frames[depth].num_names = set_numnames; ss.curr_depth = depth; /* Every set has a vdat, but some won't be populated because the namelist that instantiated */ /* the set might not have a SS statement that populates the models of the vdat. This is ok */ /* because 1) IR is supposed to be bloated so that binary out isnt 2) this functionality */ /* preserves the assumptions that insert_framesheet() makes when it calls curr_vdat() */ alloc_vdat(); } } } /* Set to 0 to reset for next set_namelist */ set_numnames = 0; } /* We create new odats for each map variant that are children of the current odat/set set their name as the map name, and identify them by marking them as a map. This lets us distinguish between sibling odats that have the same name because the map of the parent odat had the same name as another, regular odat. */ #define CURR_SS_FRAME() (ss.set_frames[ss.curr_depth]) #define CURR_SS_SETP() (CURR_SS_FRAME().setp) #define CURR_SS_ODATP() (CURR_SS_FRAME().odatp) void insert_map ( struct name* name, int direction, int height, int width, int refid, uint8_t* filepath ) { int i; struct odat* curr_map_odatp; //pointer to odat in odat_buf struct set* curr_map_setp; //pointer to set in set_buf struct link* linkp; curr_map_odatp = alloc_odat(); curr_map_setp = alloc_set(); /* Create a new odat, make its parent be the set. Make a set for mdat, its name should */ /* be the name of the odat + name of model. That makes a conflict beween odats that are named */ /* the same thing as the model of a sibling odat that was created from a map. They can have */ /* same name if the map odat is marked. So mark the map odat. */ /* insert parent odat */ curr_map_odatp->parent_odatp = CURR_SS_ODATP(); /* insert into set_list */ if(CURR_SS_SETP()->num_sets < MAX_SETS) CURR_SS_SETP()->set_list[CURR_SS_SETP()->num_sets++] = curr_map_setp; else { printf("You have allocated to many sets, error in insert_map()\n"); //TODO: EXIT() } /* indicate that newly created odat is a map */ curr_map_odatp->ismap = 1; /* set odat and set name */ name_u8_cpy(&curr_map_odatp->name, name); /* set cdat idx values for both set and odat */ curr_map_setp->cdat_idx = num_cdats; /* Insert map information into the odats map */ curr_map_odatp->map.height = height; curr_map_odatp->map.width = width; u8_stpncpy(curr_map_odatp->map.filepath, filepath, FPATH_MAX); /* Generate refid if needed, put into ref_buf */ if(!refid) refid = ss_refid++; insert_ref(curr_map_odatp, refid); /* If current odatp on stack has a link, then we need to make our own link. just set the vdat_idx */ if(CURR_SS_ODATP()->vdat_idx == 0) { linkp = alloc_link(); linkp->type = CURR_SS_ODATP()->linkp->type; linkp->dest_odatp = CURR_SS_ODATP(); linkp->dest_refid = refid; linkp->link_t.mlink.src_refid = CURR_SS_ODATP()->linkp->link_t.mlink.src_refid; /* Copy the animation name of the vlink*/ name_u8_cpy(&linkp->link_t.vlink.src_animname, &CURR_SS_ODATP()->linkp->link_t.vlink.src_animname); /* Copy the namelist of the vlink*/ for(i = 0; i < MAX_DEPTH; i++) name_u8_cpy(&linkp->link_t.vlink.src_namelist[i], &CURR_SS_ODATP()->linkp->link_t.vlink.src_namelist[i]); } else curr_map_odatp->vdat_idx = CURR_SS_ODATP()->vdat_idx; } /* 11/22 Each vdat has a multiple models. Each model has 8 framesheets, one in each direction, that create a spritesheet. Inserting framesheets into the correct model is just a matter of checking whether or not the last models name matches the current one. We can never get a framesheet that is for the same model before AND after some other model, due to alphasorting of the files in each directory */ void insert_framesheet ( struct name* model_name, int direction, int height, int width, int refid, uint8_t* filepath ) { struct vdat* curr_vdatp; struct model* curr_modelp; static struct name last_model_name[32]; curr_vdatp = curr_vdat(); /* If the model name changed, that means there are no more framesheets for that model to be processed, a guaruntee we make b/c the filenames are alphabetically sorted */ if(!name_u8_cmp(last_model_name, model_name)) { if(curr_vdatp->num_models) curr_vdatp->num_models++; num_models++; // total number of models } if(CURR_SS_ODATP()->refid == 0) { if(!refid) refid = ss_refid++; insert_ref(CURR_SS_ODATP(), refid); /* given a odatp and a refid, insert the odatp into the ref_buf. */ } else printf("error: redefining a previously set refid\n"); curr_modelp = curr_model(); name_u8_cpy(&curr_modelp->name, model_name); curr_modelp->spritesheet[direction].height = height; curr_modelp->spritesheet[direction].width = width; u8_stpncpy(curr_modelp->spritesheet[direction].filepath, filepath, FPATH_MAX); name_u8_cpy(last_model_name, model_name); } void insert_mlink ( struct name* src_mapname, int src_refid) { struct link* linkp; int i; linkp = alloc_link(); /* set type */ linkp->type = 3; /* set the name of the src map for the link, if a name exists */ if(src_mapname) name_u8_cpy(&linkp->link_t.mlink.src_mapname, src_mapname); /* Set the source ref id of the link */ linkp->link_t.mlink.src_refid = src_refid; /* Copy the entire namelist of the link, if it exists */ for(i = 0; i < link_numnames; i--) { name_u8_cpy(&linkp->link_t.mlink.src_namelist[i], &link_namelist[i]); name_u8_set(&link_namelist[i], (ucs4_t) 0); } link_numnames = 0; linkp->dest_odatp = CURR_SS_ODATP();//current odat on set_stack } void insert_link_name ( struct name* name ) { //Push name onto current namelist, set the set_namelist. name_u8_cpy(&link_namelist[link_numnames++], name); } /* Nearly identical to mlink */ void insert_vlink ( struct name* src_animname, int src_refid ) { struct link* linkp; int i; linkp = alloc_link(); /* set type */ linkp->type = 2; /* set the name of the src animname for the link, if a name exists */ if(src_animname) name_u8_cpy(&linkp->link_t.vlink.src_animname, src_animname); /* Set the source ref id of the link */ linkp->link_t.mlink.src_refid = src_refid; /* Copy the entire namelist of the link, if it exists */ for(i = 0; i < link_numnames; i++) { name_u8_cpy(&linkp->link_t.vlink.src_namelist[i], &link_namelist[i]); name_u8_set(&link_namelist[i], (ucs4_t) 0);//set to null for next link_namelist } link_numnames = 0; linkp->dest_odatp = CURR_SS_ODATP();//current odat on set_stack } /* TODO: Do we really need to store the prev/next pointer? iterating through the ref_buf could be achieved by iterating until the num_refs anyway. */ void insert_ref ( struct odat* odatp, int refid ) { struct ref* curr_refp; struct ref* prev_refp; curr_refp = alloc_ref(); prev_refp = prev_ref(); prev_refp->nextref = curr_refp; curr_refp->lastref = prev_refp; curr_refp->odatp = odatp; curr_refp->refid = refid; if(refid % 16) { POST_ALLOC(); CURRENT_POST()->refid = refid; CURRENT_POST()->odatp = odatp; } } void insert_refid ( int refid ) { CURR_SS_ODATP()->refid = refid; } void insert_vdat () { struct vdat* curr_vdatp; curr_vdatp = curr_vdat(); curr_vdatp->creator = CURR_SS_ODATP(); CURR_SS_ODATP()->vdat_idx = num_vdats; CURR_SS_ODATP()->vdatp = curr_vdatp; alloc_vdat(); }