home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2007 September / maximum-cd-2007-09.iso / Assets / data / AssaultCube_v0.93.exe / source / src / docs.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2007-05-14  |  17.0 KB  |  509 lines

  1. // docs.cpp: ingame command documentation system
  2.  
  3. #include "cube.h"
  4.  
  5. void renderdocsection(void *menu, bool init);
  6.  
  7. extern hashtable<char *, ident> *idents;
  8.  
  9. struct docargument
  10. {
  11.     char *token, *desc, *values;
  12.     bool vararg;
  13. };
  14.  
  15. struct docref
  16. {
  17.     char *name, *ident, *url, *article;
  18. };
  19.  
  20. struct docexample
  21. {
  22.     char *code, *explanation;
  23. };
  24.  
  25. struct dockey
  26. {
  27.     char *alias, *name, *desc;
  28. };
  29.  
  30. struct docident
  31. {
  32.     char *name, *desc;
  33.     vector<docargument> arguments;
  34.     cvector remarks;
  35.     vector<docref> references;
  36.     vector<docexample> examples;
  37.     vector<dockey> keys;
  38. };
  39.  
  40. struct docsection
  41. {
  42.     char *name;
  43.     vector<docident *> idents;
  44.     void *menu;
  45. };
  46.  
  47. vector<docsection> sections;
  48. hashtable<char *, docident> docidents; // manage globally instead of a section tree to ensure uniqueness
  49. docsection *lastsection = NULL;
  50. docident *lastident = NULL;
  51.  
  52. void adddocsection(char *name)
  53. {
  54.     if(!name) return;
  55.     docsection &s = sections.add();
  56.     s.name = newstring(name);
  57.     s.menu = addmenu(s.name, NULL, true, renderdocsection);
  58.     lastsection = &s;
  59. }
  60.  
  61. void adddocident(char *name, char *desc)
  62. {
  63.     if(!name || !desc || !lastsection) return;
  64.     name = newstring(name);
  65.     docident &c = docidents[name];
  66.     lastsection->idents.add(&c);
  67.     c.name = name;
  68.     c.desc = newstring(desc);
  69.     lastident = &c;
  70. }
  71.  
  72. void adddocargument(char *token, char *desc, char *values, char *vararg)
  73. {
  74.     if(!lastident || !token || !desc) return;
  75.     docargument &a = lastident->arguments.add();
  76.     a.token = newstring(token);
  77.     a.desc = newstring(desc);
  78.     a.values = values && strlen(values) ? newstring(values) : NULL;
  79.     a.vararg = vararg && atoi(vararg) == 1 ? true : false;
  80. }
  81.  
  82. void adddocremark(char *remark)
  83. {
  84.     if(!lastident || !remark) return;
  85.     lastident->remarks.add(newstring(remark));
  86. }
  87.  
  88. void adddocref(char *name, char *ident, char *url, char *article)
  89. {
  90.     if(!lastident || !name) return;
  91.     docref &r = lastident->references.add();
  92.     r.name = newstring(name);
  93.     r.ident = ident && strlen(ident) ? newstring(ident) : NULL;
  94.     r.url = url && strlen(url) ? newstring(url) : NULL;
  95.     r.article = article && strlen(article) ? newstring(article) : NULL;
  96. }
  97.  
  98. void adddocexample(char *code, char *explanation)
  99. {
  100.     if(!lastident || !code) return;
  101.     docexample &e = lastident->examples.add();
  102.     e.code = newstring(code);
  103.     e.explanation = explanation && strlen(explanation) ? newstring(explanation) : NULL;
  104. }
  105.  
  106. void adddockey(char *alias, char *name, char *desc)
  107. {
  108.     if(!lastident || !alias) return;
  109.     dockey &k = lastident->keys.add();
  110.     k.alias = newstring(alias);
  111.     k.name = name && strlen(name) ? newstring(name) : NULL;
  112.     k.desc = desc && strlen(desc) ? newstring(desc) : NULL;
  113. }
  114.  
  115. COMMANDN(docsection, adddocsection, ARG_1STR);
  116. COMMANDN(docident, adddocident, ARG_2STR);
  117. COMMANDN(docargument, adddocargument, ARG_4STR);
  118. COMMANDN(docremark, adddocremark, ARG_1STR);
  119. COMMANDN(docref, adddocref, ARG_3STR);
  120. COMMANDN(docexample, adddocexample, ARG_2STR);
  121. COMMANDN(dockey, adddockey, ARG_3STR);
  122.  
  123. int stringsort(const char **a, const char **b) { return strcmp(*a, *b); }
  124.  
  125. char *cvecstr(vector<char *> &cvec, char *substr, int *rline = NULL)
  126. {
  127.     char *r = NULL;
  128.     loopv(cvec) if(cvec[i]) if((r = strstr(cvec[i], substr)) != NULL) 
  129.     { 
  130.         if(rline) *rline = i;
  131.         break; 
  132.     }
  133.     return r;
  134. }
  135.  
  136. void docundone(int allidents)
  137. {
  138.     cvector inames;
  139.     identnames(inames, !(allidents > 0));
  140.     inames.sort(stringsort);
  141.     loopv(inames)
  142.     {
  143.         docident *id = docidents.access(inames[i]);
  144.         if(id) // search for substrings that indicate undoneness
  145.         {
  146.             cvector srch;
  147.             srch.add(id->name);
  148.             srch.add(id->desc);
  149.             loopvj(id->remarks) srch.add(id->remarks[j]);
  150.             loopvj(id->arguments) 
  151.             { 
  152.                 srch.add(id->arguments[j].token);
  153.                 srch.add(id->arguments[j].desc);
  154.                 srch.add(id->arguments[j].values);
  155.             }
  156.             loopvj(id->references)
  157.             {
  158.                 srch.add(id->references[j].ident);
  159.                 srch.add(id->references[j].name);
  160.                 srch.add(id->references[j].url);
  161.             }
  162.             if(!cvecstr(srch, "TODO") && !cvecstr(srch, "UNDONE")) continue;
  163.         }
  164.         conoutf(inames[i]);
  165.     }
  166. }
  167.  
  168. void docinvalid()
  169. {
  170.     cvector inames;
  171.     identnames(inames, true);
  172.     inames.sort(stringsort);
  173.     enumerateht(docidents) if(!strchr(docidents.enumc->data.name, ' ') && !identexists(docidents.enumc->data.name)) 
  174.         conoutf(docidents.enumc->data.name);
  175. }
  176.  
  177. void docfind(char *search)
  178. {
  179.     enumerateht(docidents)
  180.     {
  181.         docident &i = docidents.enumc->data;
  182.         cvector srch;
  183.         srch.add(i.name);
  184.         srch.add(i.desc);
  185.         loopvk(i.remarks) srch.add(i.remarks[k]);
  186.         
  187.         char *r;
  188.         int rline;
  189.         if((r = cvecstr(srch, search, &rline)))
  190.         {
  191.             const int matchchars = 200;
  192.             string match;
  193.             s_strncpy(match, r-srch[rline] > matchchars/2 ? r-matchchars/2 : srch[rline], matchchars/2);
  194.             conoutf("%-20s%s", i.name, match);
  195.         }
  196.     }
  197. }
  198.  
  199. char *xmlstringenc(char *d, const char *s, size_t len)
  200. {
  201.     if(!d || !s) return NULL;
  202.     struct spchar { char c; char repl[8]; } const spchars[] = { {'&', "&"}, {'<', "<"}, {'>', "gt;"}, {'"', """}, {'\'', "'"}};
  203.  
  204.     char *dc = d;
  205.     const char *sc = s;
  206.  
  207.     while(*sc && (size_t)(dc - d) < len - 1)
  208.     {
  209.         bool specialc = false;
  210.         loopi(sizeof(spchars)/sizeof(spchar)) if(spchars[i].c == *sc) 
  211.         {
  212.             specialc = true;
  213.             size_t rlen = strlen(spchars[i].repl);
  214.             if(dc - d + rlen <= len - 1)
  215.             {
  216.                 memcpy(dc, spchars[i].repl, rlen);
  217.                 dc += rlen;
  218.                 break;
  219.             }
  220.         }
  221.         if(!specialc) memcpy(dc++, sc, 1);
  222.         *dc = 0;
  223.         sc++;
  224.     }
  225.     return d;
  226. }
  227.  
  228. void docwritebaseref(char *ref, char *schemalocation, char *transformation)
  229. {
  230.     s_sprintfd(outfile)("docs%cautogenerated_base_reference.xml", PATHDIV);
  231.     FILE *f = fopen(outfile, "w");
  232.     if(!f) return;
  233.     char desc[] = "<description>TODO: Description</description>";
  234.  
  235.     fprintf(f, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");    
  236.     fprintf(f, "<?xml-stylesheet type=\"text/xsl\" href=\"%s\"?>\n", transformation && strlen(transformation) ? transformation : "transformations/cuberef2xhtml.xslt");
  237.     fprintf(f, "<cuberef name=\"%s\" version=\"v0.1\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"%s\" xmlns=\"http://cubers.net/Schemas/CubeRef\">\n", ref && strlen(ref) ? ref : "Unnamed Reference", schemalocation && strlen(schemalocation) ? schemalocation : "http://cubers.net/Schemas/CubeRef schemas/cuberef.xsd");
  238.     fprintf(f, "\t%s\n", desc);
  239.     fprintf(f, "\t<sections>\n");
  240.     fprintf(f, "\t\t<section name=\"Main\">\n");
  241.     fprintf(f, "\t\t\t%s\n", desc);
  242.     fprintf(f, "\t\t\t<identifiers>\n");
  243.     
  244.     string name;
  245.     enumerateht(*idents)
  246.     {
  247.         ident &id = idents->enumc->data;
  248.         if(id.type != ID_COMMAND) continue;
  249.         fprintf(f, "\t\t\t\t<command name=\"%s\">\n", xmlstringenc(name, id.name, _MAXDEFSTR));
  250.         fprintf(f, "\t\t\t\t\t%s\n", desc);
  251.         if(id.narg != ARG_NONE && id.narg != ARG_DOWN)
  252.         {
  253.             fprintf(f, "\t\t\t\t\t<arguments>\n");
  254.             if(id.narg == ARG_VARI) fprintf(f, "\t\t\t\t\t\t<variableArgument token=\"...\" description=\"TODO\"/>\n");
  255.             else
  256.             {
  257.                 int base = id.narg >= ARG_1EST ? ARG_1EST : (id.narg >= ARG_1EXP ? ARG_1EXP : (id.narg >= ARG_1STR ? ARG_1STR : ARG_1INT));
  258.                 loopj(id.narg-base+1) fprintf(f, "\t\t\t\t\t\t<argument token=\"%c\" description=\"TODO\"/>\n", (char)(*"A")+j);
  259.             }
  260.             fprintf(f, "\t\t\t\t\t</arguments>\n");
  261.         }
  262.         fprintf(f, "\t\t\t\t</command>\n");
  263.     }
  264.     enumerateht(*idents)
  265.     {
  266.         ident &id = idents->enumc->data;
  267.         if(id.type != ID_VAR) continue;
  268.         fprintf(f, "\t\t\t\t<variable name=\"%s\">\n", xmlstringenc(name, id.name, _MAXDEFSTR));
  269.         fprintf(f, "\t\t\t\t\t<description>TODO</description>\n");
  270.         fprintf(f, "\t\t\t\t\t<value %s description=\"TODO\" minValue=\"%i\" maxValue=\"%i\" defaultValue=\"%i\" %s/>\n", id.min>id.max ? "" : "token=\"N\"", id.min, id.max, *id.storage, id.min>id.max ? "readOnly=\"true\"" : "");
  271.         fprintf(f, "\t\t\t\t</variable>\n");
  272.     }
  273.  
  274.     fprintf(f, "\t\t\t</identifiers>\n\t\t</section>\n\t</sections>\n</cuberef>\n");
  275.     fclose(f);
  276. }
  277.  
  278. COMMAND(docundone, ARG_1INT);
  279. COMMAND(docinvalid, ARG_NONE);
  280. COMMAND(docfind, ARG_1STR);
  281. COMMAND(docwritebaseref, ARG_3STR);
  282. VAR(docvisible, 0, 1, 1);
  283. VAR(docskip, 0, 0, 1000);
  284.  
  285. void toggledoc() { docvisible = !docvisible; }
  286. void scrolldoc(int i) { docskip += i; if(docskip < 0) docskip = 0; }
  287.  
  288. int numargs(char *args)
  289. {
  290.     if(!args || !strlen(args)) return -1;
  291.  
  292.     int argidx = -1;
  293.     char *argstart = NULL;
  294.  
  295.     for(char *t = args; *t; t++)
  296.     {
  297.         if(!argstart && *t != ' ') { argstart = t; argidx++; }
  298.         else if(argstart && *t == ' ') if(t-1 >= args) 
  299.         {
  300.             switch(*argstart)
  301.             {
  302.                 case '[': if(*(t-1) != ']') continue; break;
  303.                 case '"': if(*(t-1) != '"') continue; break;
  304.                 default: break;
  305.             }
  306.             argstart = NULL;
  307.         }
  308.     }
  309.     return argidx;
  310. }
  311.  
  312. void renderdoc(int x, int y)
  313. {
  314.     if(!docvisible) return;
  315.  
  316.     char *exp = getcurcommand();
  317.     if(!exp || *exp != '/' || strlen(exp) < 2) return;
  318.  
  319.     char *c = exp+1;
  320.     size_t clen = strlen(c);
  321.     for(size_t i = 0; i < clen; i++) // search first matching cmd doc by stripping arguments of exp from right to left
  322.     {
  323.         char *end = c+clen-i;
  324.         if(!*end || *end == ' ')
  325.         {
  326.             string cmd;
  327.             s_strncpy(cmd, c, clen-i+1);
  328.             docident *ident = docidents.access(cmd);
  329.             if(ident)
  330.             {
  331.                 const int linemax = VIRTW*4/3;
  332.                 cvector doclines;
  333.  
  334.                 char *label = doclines.add(newstringbuf(ident->name)); // label
  335.                 loopvj(ident->arguments)
  336.                 {
  337.                     s_strcat(label, " ");
  338.                     s_strcat(label, ident->arguments[j].token);
  339.                 }
  340.                 doclines.add(NULL);
  341.                 
  342.                 cvector desc;
  343.                 text_block(ident->desc, linemax, desc); // desc
  344.                 loopvj(desc) doclines.add(desc[j]);
  345.                 doclines.add(NULL);
  346.  
  347.                 if(ident->arguments.length() > 0) // args
  348.                 {
  349.                     extern int commandpos;
  350.                     char *args = strchr(c, ' ');
  351.                     int arg = -1;
  352.  
  353.                     if(args)
  354.                     {
  355.                         args++;
  356.                         if(commandpos >= 0)
  357.                         {
  358.                             if(commandpos >= args-c)
  359.                             {
  360.                                 string a;
  361.                                 s_strncpy(a, args, commandpos-(args-c)+1);
  362.                                 args = a;
  363.                                 arg = numargs(args);
  364.                             }
  365.                         }
  366.                         else arg = numargs(args);
  367.                         
  368.                         if(arg >= 0) // multipart idents need a fixed argument offset
  369.                         {
  370.                             char *c = cmd;
  371.                             while((c = strchr(c, ' ')) && c++) arg--;
  372.                         }
  373.                         
  374.                         // fixes offset for var args
  375.                         if(arg >= ident->arguments.length() && ident->arguments.last().vararg) arg = ident->arguments.length() - 1;
  376.                     }
  377.  
  378.                     loopvj(ident->arguments) 
  379.                     {
  380.                         docargument *a = &ident->arguments[j];
  381.                         if(!a) continue;
  382.                         char *argstr = doclines.add(new string);
  383.                         s_sprintf(argstr)("\f%d%-8s%s %s%s%s", j == arg ? 4 : 5, a->token, a->desc,
  384.                             a->values ? "(" : "", a->values ? a->values : "", a->values ? ")" : "");
  385.                     }
  386.  
  387.                     doclines.add(NULL);
  388.                 }
  389.  
  390.                 if(ident->remarks.length()) // remarks
  391.                 {
  392.                     loopvj(ident->remarks) 
  393.                     {
  394.                          cvector remarks;
  395.                          text_block(ident->remarks[j], linemax, remarks);
  396.                          loopvk(remarks) doclines.add(remarks[k]);
  397.                     }
  398.                     doclines.add(NULL);
  399.                 }
  400.  
  401.                 if(ident->examples.length()) // examples
  402.                 {   
  403.                     doclines.add(newstring(ident->examples.length() == 1 ? "Example:" : "Examples:"));
  404.                     loopvj(ident->examples)
  405.                     {
  406.                         cvector lines;
  407.                         text_block(ident->examples[j].code, linemax, lines);
  408.                         text_block(ident->examples[j].explanation, linemax, lines);
  409.                         loopvk(lines) doclines.add(lines[k]);
  410.                     }
  411.                     doclines.add(NULL);
  412.                 }
  413.  
  414.                 if(ident->keys.length()) // default keys
  415.                 {
  416.                     doclines.add(newstring(ident->keys.length() == 1 ? "Default key:" : "Default keys:"));
  417.                     loopvj(ident->keys)
  418.                     {
  419.                         dockey &k = ident->keys[j];
  420.                         s_sprintfd(line)("%-10s %s", k.name ? k.name : k.alias, k.desc ? k.desc : "");
  421.                         doclines.add(newstring(line));
  422.                     }
  423.                     doclines.add(NULL);
  424.                 }
  425.  
  426.                 if(ident->references.length()) // references
  427.                 {
  428.                     struct category { string label; string refs; }
  429.                     categories[] = {{"related identifiers", ""} , {"web resources", ""}, {"wiki articles", ""}, {"other", ""}};
  430.                     loopvj(ident->references)
  431.                     {
  432.                         docref &r = ident->references[j];
  433.                         char *ref = r.ident ? categories[0].refs : (r.url ? categories[1].refs : (r.article ? categories[2].refs : categories[3].refs));
  434.                         s_strcat(ref, r.name);
  435.                         if(j < ident->references.length()-1) s_strcat(ref, ", ");
  436.                     }
  437.                     loopj(sizeof(categories)/sizeof(category))
  438.                     {
  439.                         if(!strlen(categories[j].refs)) continue;
  440.                         char *line = doclines.add(newstringbuf(categories[j].label));
  441.                         s_strcat(line, ": ");
  442.                         s_strcat(line, categories[j].refs);
  443.                     }
  444.                 }
  445.  
  446.                 int screenlines = (VIRTH*2/3/FONTH)-1;
  447.                 if(docskip) docskip = min(docskip, doclines.length() - screenlines); // normalize
  448.                 bool more = docskip < doclines.length() - screenlines;
  449.                
  450.                 for(int j = docskip; j < min(doclines.length(), docskip+screenlines); j++)
  451.                 {
  452.                     if(doclines[j]) draw_textf("%s", x, y+j*FONTH, doclines[j]);
  453.                 }
  454.                 doclines.deletecontentsa();
  455.  
  456.                 if(more) draw_textf("\f4more (F3)", x, y+screenlines*FONTH); // footer
  457.                 if(docskip > 0) draw_textf("\f4less (F2)", x, y+(screenlines+1)*FONTH);
  458.                 draw_textf("\f4disable doc reference (F1)", x, y+(screenlines+2)*FONTH);
  459.                 return;
  460.             }
  461.         }
  462.     }
  463. }
  464.  
  465. void *docmenu = NULL;
  466.  
  467. struct msection { char *name; string cmd; };
  468.  
  469. int msectionsort(const msection *a, const msection *b)
  470. {
  471.     return strcmp(a->name, b->name);
  472. }
  473.  
  474. void renderdocsection(void *menu, bool init)
  475. {
  476.     static vector<msection> msections;
  477.     msections.setsize(0);
  478.     
  479.     loopv(sections)
  480.     {
  481.         if(sections[i].menu != menu) continue;
  482.         loopvj(sections[i].idents)
  483.         {
  484.             docident &id = *sections[i].idents[j];
  485.             msection &s = msections.add();
  486.             s.name = id.name;
  487.             s_sprintf(s.cmd)("saycommand [/%s ]", id.name);
  488.         }
  489.         msections.sort(msectionsort);
  490.         loopv(msections) { menumanual(menu, i, msections[i].name, msections[i].cmd); }
  491.         return;
  492.     }
  493. }
  494.  
  495. struct maction { string cmd; };
  496.  
  497. void renderdocmenu(void *menu, bool init)
  498. {
  499.     static vector<maction> actions;
  500.     actions.setsize(0);
  501.  
  502.     loopv(sections)
  503.     {
  504.         maction &a = actions.add();
  505.         s_sprintf(a.cmd)("showmenu [%s]", sections[i].name);
  506.         menumanual(menu, i, sections[i].name, a.cmd);
  507.     }
  508. }
  509.