#include "parse/scripting.h" #ifdef USE_LUA #include #include "parse/lua.h" #include "graphics/2d.h" #include "ship/ship.h" #include "gamesequence/gamesequence.h" #include "globalincs/pstypes.h" //*************************Lua funcs************************* int script_parse_args(lua_State *L, char *fmt, ...); int script_return_args(lua_State *L, char* fmt, ...); int script_remove_lib(lua_State *L, char *name); void lua_stackdump(lua_State *L); //*************************Lua helpers************************* typedef struct script_lua_func_list { const char *name; lua_CFunction func; const char *description; } script_lua_func_list; typedef struct script_lua_obj_func_list { const char *name; lua_CFunction func; int type; const char *description; } script_lua_obj_func_list; typedef struct script_lua_obj_list { const char *object_name; const script_lua_obj_func_list *object_funcs; const char *description; }script_lua_obj_list; typedef struct script_lua_lib_list { const char *library_name; const script_lua_func_list *library_funcs; const script_lua_obj_list *library_objects; const char *description; }script_lua_lib_list; //Used with the args functions to check a function type struct script_lua_udata { char *meta; void **buf; script_lua_udata(char* in_meta, void** in_buf){meta=in_meta; buf=in_buf;} }; //LUA_UDATA(name, pointer) //Call this with script_parse_args to store the pointer to //a specific set of userdata in a pointer. //-----EX: //script_parse_args(L, "u", LUA_UDATA("ship", &lua_ship)) #define LUA_UDATA(n, p) script_lua_udata(n, (void**)&p) #define LUA_RETURN_OBJECT 1 //LUA_NEW_OBJ(lua_State, object name, object struct) #define LUA_NEW_OBJ(L,n,s) \ (s *) lua_newuserdata(L, sizeof(s)); \ luaL_getmetatable(L, n); \ lua_setmetatable(L, -2) //*************************Lua defines************************* enum { LUA_OPER_METHOD =0, LUA_OPER_ADDITION, LUA_OPER_SUBTRACTION, LUA_OPER_MULTIPLICATION, LUA_OPER_DIVISION, LUA_OPER_POWER, LUA_OPER_UNARY, LUA_OPER_CONCATENATION, LUA_OPER_EQUALTO, LUA_OPER_LESSTHAN, LUA_OPER_LESSTHANEQUALTO, LUA_OPER_INDEX, LUA_OPER_NEWINDEX, LUA_OPER_CALL }; char *Lua_opers[] = { "__index", "__add", "__sub", "__mult", "__div", "__pow", "__unm", "__concat", "__eq", "__lt", "__le", "__index", "__newindex", "__call", }; //*************************Functions************************* int script_remove_lib(lua_State *L, char *name) { lua_pushstring(L, name); lua_gettable(L, LUA_GLOBALSINDEX); if(lua_istable(L, -1)) { lua_pop(L, -1); return 1; } return 0; } void lua_stackdump(lua_State *L) { char stackdump[10240] = "\0"; char buf[512]; int stacksize = lua_gettop(L); //Lua temps double d; int b; char *s; void *v; lua_State *ls; for(int argnum = 1; argnum <= stacksize; argnum++) { int type = lua_type(L, argnum); switch(type) { case LUA_TNIL: strcat(stackdump, "\r\nNIL"); break; case LUA_TNUMBER: d = lua_tonumber(L, argnum); sprintf(buf, "\r\nNumber [%f]",d); strcat(stackdump, buf); break; case LUA_TBOOLEAN: b = lua_toboolean(L, argnum); sprintf(buf, "\r\nBoolean [%d]",b); strcat(stackdump, buf); break; case LUA_TSTRING: s = (char *)lua_tostring(L, argnum); sprintf(buf, "\r\nString [%s]",s); strcat(stackdump, buf); break; case LUA_TTABLE: strcat(stackdump, "\r\nTable"); break; case LUA_TFUNCTION: strcat(stackdump, "\r\nFunction"); break; case LUA_TUSERDATA: v = lua_touserdata(L, argnum); sprintf(buf, "\r\nUserdata [%d]", v); strcat(stackdump, buf); break; case LUA_TTHREAD: ls = lua_tothread(L, argnum); sprintf(buf, "\r\nThread [%d]", ls); strcat(stackdump, buf); break; case LUA_TLIGHTUSERDATA: v = lua_touserdata(L, argnum); sprintf(buf, "\r\nLight userdata [%d]", v); strcat(stackdump, buf); break; default: sprintf(buf, "\r\n: %s (%d) (%s)", lua_typename(L, type), lua_tonumber(L, argnum), lua_tostring(L, argnum)); strcat(stackdump, buf); break; } } Warning(LOCATION, "LUA STACKDUMP:%s",stackdump); } int script_return_args(lua_State *L, char *fmt, ...) { //Start throught va_list vl; int nargs; va_start(vl, fmt); nargs = 1; while(*fmt) { switch(*fmt++) { case 'b': lua_pushboolean(L, va_arg(vl, double) ? 1 : 0); break; case 'd': lua_pushnumber(L, va_arg(vl, double)); break; case 'f': lua_pushnumber(L, va_arg(vl, float)); break; case 'i': lua_pushnumber(L, va_arg(vl, int)); break; case 's': lua_pushstring(L, va_arg(vl, char *)); break; default: Error(LOCATION, "Bad character passed to script_return_args; (%c)", *fmt); } nargs++; } va_end(vl); return nargs; } //script_parse_args(state, arguments, variables) //---------------------------------------------- //based on "Programming in Lua" // //Parses arguments from string to variables given //a '|' divides required and optional arguments. //Returns 0 if a required argument is invalid, //or there are too few arguments actually passed int script_parse_args(lua_State *L, char *fmt, ...) { //Check that we have all the arguments that we need //If we don't, return 0 int needed_args = strlen(fmt); int total_args = lua_gettop(L); if(strchr(fmt, '|') != NULL) { needed_args = strchr(fmt, '|') - fmt; } if(total_args < needed_args) { Warning(LOCATION, "Not enough arguments for function"); lua_stackdump(L); return 0; } //Start throught va_list vl; int nargs; //Are we parsing optional args yet? bool optional_args = false; va_start(vl, fmt); nargs = 1; while(*fmt && nargs <= total_args) { //Skip functions; I assume these are being used to return args while(lua_type(L, nargs) == LUA_TFUNCTION && nargs <= total_args) nargs++; switch(*fmt++) { case 'b': if(lua_isboolean(L, nargs)) { *va_arg(vl, bool*) = lua_toboolean(L, nargs) > 0 ? true : false; } else { Warning(LOCATION, "Argument %d is an invalid type; boolean expected"); if(!optional_args) return 0; } break; case 'd': if(lua_isnumber(L, nargs)) { *va_arg(vl, double*) = (double)lua_tonumber(L, nargs); } else { Warning(LOCATION, "Argument %d is an invalid type; float expected", nargs); if(!optional_args) return 0; } break; case 'f': if(lua_isnumber(L, nargs)) { *va_arg(vl, float*) = (float)lua_tonumber(L, nargs); } else { Warning(LOCATION, "Argument %d is an invalid type; float expected", nargs); if(!optional_args) return 0; } break; case 'i': if(lua_isnumber(L, nargs)) { *va_arg(vl, int*) = (int)lua_tonumber(L, nargs); } else { Warning(LOCATION, "Argument %d is an invalid type; int expected", nargs); if(!optional_args) return 0; } break; case 's': if(lua_isstring(L, nargs)) { *va_arg(vl, const char **) = lua_tostring(L, nargs); } else { Warning(LOCATION, "Argument %d is an invalid type; string expected", nargs); lua_stackdump(L); if(!optional_args) return 0; } break; case 'u': if(lua_isuserdata(L, nargs)) { script_lua_udata ud = va_arg(vl, script_lua_udata); (*ud.buf) = luaL_checkudata(L, nargs, ud.meta); if((*ud.buf) == NULL) { Warning(LOCATION, "Argument %d is the wrong type of userdata; %s expected", nargs, ud.meta); } } else { Warning(LOCATION, "Argument %d is an invalid type %d; type '%s' expected", nargs, lua_type(L, nargs), va_arg(vl, script_lua_udata).meta); if(!optional_args) return 0; } break; case '|': nargs--; //cancel out the nargs++ at the end optional_args = true; break; default: Error(LOCATION, "Bad character passed to script_parse_args; (%c)", *fmt); } nargs++; } va_end(vl); return nargs; } //*************************Libraries************************* //**********LIBRARY: Base static int lua_fs2_print(lua_State *L) { Error(LOCATION, "LUA: %s", lua_tostring(L, -1)); return 0; } static int lua_fs2_error(lua_State *L) { Error(LOCATION, "LUA ERROR: %s", lua_tostring(L, -1)); return 0; } static int lua_fs2_getState(lua_State *L) { int depth = 0; script_parse_args(L, "|i", &depth); return script_return_args(L, "s", GS_state_text[gameseq_get_state(depth)]); } static int lua_fs2_lvec(lua_State *L) { vec3d v3 = vmd_zero_vector; script_parse_args(L, "|fff", &v3.xyz.x, &v3.xyz.y, &v3.xyz.z); vec3d* v3p = LUA_NEW_OBJ(L, "fs2.lvec", vec3d); v3p->xyz.x = v3.xyz.x; v3p->xyz.y = v3.xyz.y; v3p->xyz.z = v3.xyz.z; return LUA_RETURN_OBJECT; } static int lua_fs2_lvec_print(lua_State *L) { vec3d *v3p; if(!script_parse_args(L, "u", LUA_UDATA("fs2.lvec", v3p))) return 0; Error(LOCATION, "VECTOR: %f %f %f", v3p->xyz.x, v3p->xyz.y, v3p->xyz.z); return 0; } static int lua_fs2_lvec_get(lua_State *L) { vec3d *v3p; int idx; if(!script_parse_args(L, "ui", LUA_UDATA("fs2.lvec", v3p), &idx)) return 0; if(idx < 3 && idx > -1) { Error(LOCATION, "VECTOR: %f", v3p->a1d[idx]); } return 0; } static int lua_fs2_wvec(lua_State *L) { vec3d v3 = vmd_zero_vector; script_parse_args(L, "|fff", &v3.xyz.x, &v3.xyz.y, &v3.xyz.z); vec3d* v3p = LUA_NEW_OBJ(L, "fs2.wvec", vec3d); v3p->xyz.x = v3.xyz.x; v3p->xyz.y = v3.xyz.y; v3p->xyz.z = v3.xyz.z; return LUA_RETURN_OBJECT; } static const script_lua_obj_func_list lua_fs2_wvec_funcs[] = { {SCRIPT_END_LIST}, }; static const script_lua_obj_func_list lua_fs2_lvec_funcs[] = { {"print", lua_fs2_lvec_print, LUA_OPER_METHOD, "Prints contents of vector"}, //{"get", lua_fs2_lvec_get, LUA_OPER_INDEX}, {SCRIPT_END_LIST}, }; static const script_lua_obj_list lua_base_lib_obj[] = { {"fs2.lvec", lua_fs2_lvec_funcs, "Local vector"}, {"fs2.wvec", lua_fs2_wvec_funcs, "World vector"}, {SCRIPT_END_LIST}, }; static const script_lua_func_list lua_base_lib[] = { {"lvec", lua_fs2_lvec, "Creates a new local vector: (x, y, z)"}, {"wvec", lua_fs2_wvec, "Creates a new world vector: (x, y, z)"}, {"print", lua_fs2_print, "Displays output"}, {"error", lua_fs2_error, "Causes an error"}, {"getState", lua_fs2_getState, "Returns the current FS2 state string"}, {SCRIPT_END_LIST}, }; //**********LIBRARY: Mission (msn) typedef struct lua_ship { ship *shipp; } lua_ship; typedef struct lua_ship_info { ship_info *sip; } lua_ship_info; static int lua_msn_getShipInfo(lua_State *L) { char *ship_name = NULL; if(!script_parse_args(L, "s", &ship_name)) return 0; int idx = ship_info_lookup(ship_name); if(idx < 0) return 0; lua_ship_info *lsip = (lua_ship_info*) lua_newuserdata(L, sizeof(lua_ship_info)); luaL_getmetatable(L, "ship_info"); lua_setmetatable(L, -2); lsip->sip = &Ship_info[idx]; return 1; } static int lua_msn_newShip(lua_State *L) { char *ship_name = NULL; char *class_name = NULL; vec3d pos; angles ang = {0,0,0}; if(!script_parse_args(L, "ssfff|fff", &ship_name, &class_name, &pos.xyz.x, &pos.xyz.y, &pos.xyz.z, &ang.p, &ang.b, &ang.h)) return 0; //New matrix matrix ori = IDENTITY_MATRIX; vm_angles_2_matrix(&ori, &ang); //Find the class index int si_idx = ship_info_lookup(class_name); if(si_idx < 0) { //couldn't find it return 0; } //Create the ship int s_idx = ship_create(&ori, &pos, si_idx, ship_name); //Make the lua ship object, if the ship was created if(s_idx > -1) { //Make the lua ship object and set the ptr to NULL lua_ship *lsp = LUA_NEW_OBJ(L, "fs2.ship", lua_ship); lsp->shipp = &Ships[s_idx]; return 1; } return 0; } static lua_msn_ship_setSpeed(lua_State *L) { lua_ship *lsp = NULL; vec3d vel; if(!script_parse_args(L, "ufff", LUA_UDATA("fs2.ship", lsp), &vel.xyz.x, &vel.xyz.y, &vel.xyz.z)) return 0; //Set the speed Objects[lsp->shipp->objnum].phys_info.vel = vel; //We're done! return 0; } static lua_msn_ship_setName(lua_State *L) { lua_ship *lsp = NULL; char *name = NULL; if(!script_parse_args(L, "us", LUA_UDATA("fs2.ship", lsp), &name)) return 0; //Set the name if(strlen(name)) { strcpy(lsp->shipp->ship_name, name); } else { sprintf(lsp->shipp->ship_name, "Ship %d", SHIP_INDEX(lsp->shipp)); } //We're done! return 0; } static lua_msn_ship_getTarget(lua_State *L) { lua_ship *lsp = NULL; if(!script_parse_args(L, "u", LUA_UDATA("fs2.ship", lsp))) return 0; if(lsp->shipp->ai_index != -1) { ai_info *aip= &Ai_info[lsp->shipp->ai_index]; if(aip->target_objnum && Objects[aip->target_objnum].type == OBJ_SHIP) { lua_ship *lsp = LUA_NEW_OBJ(L, "fs2.shp", lua_ship); lsp->shipp = &Ships[Objects[aip->target_objnum].instance]; return 1; } } //We're done! return 0; } static const script_lua_obj_func_list lua_msn_ship_funcs[] = { {"setSpeed", lua_msn_ship_setSpeed, LUA_OPER_METHOD, "Sets ship speed: (x speed, y speed, z speed)"}, {"setName", lua_msn_ship_setName, LUA_OPER_METHOD, "Changes ship name: (new name)"}, {"getTarget", lua_msn_ship_getTarget, LUA_OPER_METHOD, "Returns handle to ship's targetted ship"}, {SCRIPT_END_LIST}, }; //LIBRARY define static const script_lua_obj_list lua_msn_lib_obj[] = { //{"fs2.lvec", lua_fs2_lvec_funcs}, {"fs2.ship", lua_msn_ship_funcs, "Ship handle"}, {SCRIPT_END_LIST}, }; static const script_lua_func_list lua_msn_lib[] = { //{"lvec", lua_fs2_lvec}, {"newShip", lua_msn_newShip, "Creates a new ship and returns a handle to it: (ship name, class name, x pos, y pos, z pos; x rot, y rot, z rot)"}, {SCRIPT_END_LIST}, }; //**********LIBRARY: Graphics (grl) static int lua_grpc_setColor(lua_State *L) { if(!Gr_inited) return 0; int r,g,b,a=255; if(!script_parse_args(L, "iii|i", &r, &g, &b, &a)) return 0; color ac; gr_init_alphacolor(&ac,r,g,b,a); gr_set_color_fast(&ac); return 0; } static int lua_grpc_setFont(lua_State *L) { if(!Gr_inited) return 0; int fn; if(!script_parse_args(L, "i", &fn)) return 0; gr_set_font(fn); return 0; } static int lua_grpc_drawPixel(lua_State *L) { if(!Gr_inited) return 0; int x,y; bool r=true; if(!script_parse_args(L, "ii|b", &x, &y, &r)) return 0; gr_pixel(x,y,r); return 0; } static int lua_grpc_drawLine(lua_State *L) { if(!Gr_inited) return 0; int x1,y1,x2,y2; bool r=true; if(!script_parse_args(L, "iiii|b", &x1, &y1, &x2, &y2, &r)) return 0; gr_line(x1,y1,x2,y2,r); return 0; } static int lua_grpc_drawGradientLine(lua_State *L) { if(!Gr_inited) return 0; int x1,y1,x2,y2; bool r=true; if(!script_parse_args(L, "iiii|b", &x1, &y1, &x2, &y2, &r)) return 0; gr_gradient(x1,y1,x2,y2,r); return 0; } static int lua_grpc_drawCircle(lua_State *L) { if(!Gr_inited) return 0; int x,y,ra; bool r=true; if(!script_parse_args(L, "iii|b", &x,&y,&ra,&r)) return 0; gr_circle(x,y, ra, r); return 0; } static int lua_grpc_drawCurve(lua_State *L) { if(!Gr_inited) return 0; int x,y,ra,d; if(!script_parse_args(L, "iiii|b", &x,&y,&ra,&d)) return 0; gr_curve(x,y,ra,d); return 0; } static int lua_grpc_drawText(lua_State *L) { if(!Gr_inited) return 0; int x,y; char *s; bool r=true; if(!script_parse_args(L, "iis|b", &x, &y, &s, &r)) return 0; gr_string(x,y,s,r); return 0; } static int lua_grpc_flashScreen(lua_State *L) { if(!Gr_inited) return 0; int r,g,b; if(!script_parse_args(L, "iii", &r, &g, &b)) return 0; gr_flash(r,g,b); return 0; } //LIBRARY define static const script_lua_func_list lua_grpc_lib[] = { {"setColor", lua_grpc_setColor, "Sets the current drawing color: (red, green, blue; alpha)"}, {"setFont", lua_grpc_setFont, "Sets the current drawing font: (font number)"}, {"drawPixel", lua_grpc_drawPixel, "Draws a pixel using the current color: (x, y; resize)"}, {"drawLine", lua_grpc_drawLine, "Draws a line using the current color: (x1, y1, x2, y2; resize)"}, {"drawGradientLine", lua_grpc_drawGradientLine, "Draws a line using the current color that gradually fades out: (x1, y1, x2, y2; resize)"}, {"drawCircle", lua_grpc_drawCircle, "Draws a circle using the current color: (x,y,radius; resize)"}, {"drawCurve", lua_grpc_drawCurve, "Draws a curve using the current color: (x,y,radius,direction)"}, {"drawText", lua_grpc_drawText, "Draws text using the current font and color: (x, y, text; resize)"}, {"flashScreen", lua_grpc_flashScreen, "Flashes the screen with the specified color: (red,green,blue)"}, {SCRIPT_END_LIST}, }; //**********LIBRARY Array //Add an item in here to add an item to scripting. //Elements: {library name, library function list, library object list} const script_lua_lib_list Lua_libraries[] = { {"grpc", lua_grpc_lib, NULL, "Graphics library"}, {"misn", lua_msn_lib, lua_msn_lib_obj, "Mission library"}, {NULL, lua_base_lib, lua_base_lib_obj, "Base FS2 library"}, {SCRIPT_END_LIST}, }; #endif //USE_LUA //*************************Housekeeping************************* //Inits LUA //Note that "libraries" must end with a {NULL, NULL} //element int script_state::CreateLuaState(const script_lua_lib_list *libraries) { #ifdef USE_LUA lua_State *L = lua_open(); /* opens Lua */ luaopen_base(L); /* opens the basic library */ luaopen_table(L); /* opens the table library */ //LUAJIT hates io :( luaopen_io(L); /* opens the I/O library */ luaopen_string(L); /* opens the string lib. */ luaopen_math(L); /* opens the math lib. */ if(L == NULL) { Warning(LOCATION, "Could not initialize Lua"); return 0; } //INITIALIZE ALL LIBRARY FUNCTIONS const script_lua_func_list* func = NULL; const script_lua_obj_list* obj = NULL; const script_lua_obj_func_list *ofunc = NULL; const script_lua_lib_list* lib = libraries; for(; lib->library_funcs != NULL; lib++) { //If a library name is given, register functions as library items //If not, register functions as globals if(lib->library_name != NULL && strlen(lib->library_name)) { //Register library functions if(lib->library_funcs != NULL) { //luaL_register(L, lib->library_name, lib->library_funcs); //luaL_openlib(L, lib->library_name, lib->library_funcs, 0); //NOTE FROM WMC: //The following is based on luaL_openlib from lauxlib.c //The default library can't be used because my custom script //function array features a field for function description //Check for the library's existence lua_pushstring(L, lib->library_name); lua_gettable(L, LUA_GLOBALSINDEX); //If it doesn't exist... if (lua_isnil(L, -1)) { lua_pop(L, 1); //Pop the nil resultfrom the stack lua_newtable(L); //Create a new table lua_pushstring(L, lib->library_name); //Add a string to the stack lua_pushvalue(L, -2); //Push the table lua_settable(L, LUA_GLOBALSINDEX); //Register the table with the new name } for(func = lib->library_funcs; func->func != NULL; func++) { //Add each function lua_pushstring(L, func->name); //Push the function's name onto the stack lua_pushcclosure(L, func->func, 0); //Push the function pointer onto the stack lua_settable(L, -3); //Add it into the current lib table } } } else { //Iterate through the function list for(func = lib->library_funcs; func->func != NULL; func++) { //Sanity checking Assert(func->name != NULL && strlen(func->name)); Assert(func->func != NULL); //Register the function with the name given as a global lua_register(L, func->name, func->func); } } //Handle objects and their methods in a library if(lib->library_objects != NULL) { for(obj = (script_lua_obj_list*)lib->library_objects; obj->object_funcs != NULL; obj++) { if(!luaL_newmetatable(L, obj->object_name)) { Warning(LOCATION, "Couldn't create metatable for object '%s'", obj->object_name); continue; } //Get the absolute position of the object metatable for later use int table_loc = lua_gettop(L); ofunc = (script_lua_obj_func_list*)obj->object_funcs; //***Check for methods and indexes at the same time, I think these are invalid bool methods = false; for (; ofunc->name; ofunc++) { if(ofunc->type == LUA_OPER_METHOD) { methods = true; break; } } if(methods) { for(ofunc = (script_lua_obj_func_list*)obj->object_funcs; ofunc->name; ofunc++) { if(ofunc->type == LUA_OPER_INDEX && methods) { Error(LOCATION, "Attempt to use both methods and indexing in \"%s\"; contact a coder", obj->object_name); } } } //***Add the functions into the metatables bool table_set; uint oper_max = sizeof(Lua_opers)/sizeof(char*); for(uint i = 0; i < oper_max; i++) { table_set = false; for (ofunc = (script_lua_obj_func_list*)obj->object_funcs; ofunc->name; ofunc++) { if(ofunc->type == i && i == LUA_OPER_METHOD) { if(!table_set) { lua_pushstring(L, Lua_opers[i]); lua_pushvalue(L, table_loc); // pushes the metatable lua_settable(L, table_loc); // metatable.__index = metatable table_set = true; } lua_pushstring(L, ofunc->name); lua_pushcclosure(L, ofunc->func, 0); lua_settable(L, -3); } else if(ofunc->type == i) { //TODO: Get this working /*lua_pushstring(L, Lua_opers[i]); lua_pushcfunction(L, ofunc->func); lua_settable(L, -2);*/ } } } } } } SetLuaSession(L, libraries); return 1; #else return 0; #endif } #ifdef USE_LUA void output_lib_meta(FILE *fp, const script_lua_lib_list *lib) { const script_lua_func_list *func; const script_lua_obj_func_list *ofunc; const script_lua_obj_list *obj; if(lib->library_funcs != NULL) { fputs("
", fp); for(func = lib->library_funcs; func->name != NULL; func++) { fprintf(fp, "
%s()
%s
", func->name, func->description); } fputs("
", fp); } if(lib->library_objects != NULL) { fputs("
", fp); for(obj = lib->library_objects; obj->object_name != NULL; obj++) { fprintf(fp, "

%s

", lib->library_name, obj->object_name, obj->object_name); fputs("
", fp); for(ofunc = obj->object_funcs; ofunc->name != NULL; ofunc++) { fprintf(fp, "
%s()
%s
", ofunc->name, ofunc->description); } fputs("
", fp); } fputs("
", fp); } } #endif void script_state::OutputLuaMeta(FILE *fp) { #ifdef USE_LUA const script_lua_lib_list *lib; const script_lua_obj_list *obj; bool unnamed_display = false; //***Output quick library-object list //Output objects of non-named libraries fputs("
", fp); for(lib = LuaLibs; lib->library_funcs != NULL; lib++) { if(lib->library_name != NULL && strlen(lib->library_name)) continue; if(!unnamed_display) { fputs("
Base
", fp); unnamed_display = true; } if(lib->library_objects != NULL) { for(obj = lib->library_objects; obj->object_name != NULL; obj++) { fprintf(fp, "
%s - %s
", lib->library_name, obj->object_name, obj->object_name, obj->description); } } } //For named libaries for(lib = LuaLibs; lib->library_funcs != NULL; lib++) { if(lib->library_name == NULL || !strlen(lib->library_name)) continue; fprintf(fp, "
%s - %s
", lib->library_name, lib->library_name, lib->description); if(lib->library_objects != NULL) { for(obj = lib->library_objects; obj->object_name != NULL; obj++) { fprintf(fp, "
%s - %s
", lib->library_name, obj->object_name, obj->object_name, obj->description); } } } fputs("


", fp); //***Output libs fputs("
", fp); //Unnamed libs unnamed_display = false; for(lib = LuaLibs; lib->library_funcs != NULL; lib++) { if(lib->library_name != NULL && strlen(lib->library_name)) continue; if(!unnamed_display) { fputs("

Base

", fp); unnamed_display = true; } output_lib_meta(fp, lib); } //Named libs for(lib = LuaLibs; lib->library_funcs != NULL; lib++) { if(lib->library_name == NULL || !strlen(lib->library_name)) continue; fprintf(fp, "

%s - %s

", lib->library_name, lib->library_name, lib->description); output_lib_meta(fp, lib); } fputs("
", fp); #endif }