mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 04:49:39 -10:00
1) Remove the extra branch in `check_ok_to_fire` that was added in SVN revision 10392, or commit 0d54630e83. If there was an actual Coverity error, this does not fix it at the proper location. More importantly, this breaks certain turret firing behavior.
2) Fix a couple of bounds checks.
3) Remove an incorrect stale comment.
This fixes the broken turret swarm weapons that were uncovered by the fixes in #2959.
532 lines
13 KiB
C++
532 lines
13 KiB
C++
#include "doc_html.h"
|
|
|
|
namespace scripting {
|
|
namespace {
|
|
|
|
void ade_output_toc(FILE* fp, const std::unique_ptr<DocumentationElement>& el)
|
|
{
|
|
Assert(fp != nullptr);
|
|
Assert(el != nullptr);
|
|
|
|
// WMC - sanity checking
|
|
if (el->name.empty() && el->shortName.empty()) {
|
|
Warning(LOCATION, "Found ade_table_entry with no name or shortname");
|
|
return;
|
|
}
|
|
|
|
fputs("<dd>", fp);
|
|
|
|
if (el->name.empty()) {
|
|
fprintf(fp, "<a href=\"#%s\">%s", el->shortName.c_str(), el->shortName.c_str());
|
|
} else {
|
|
fprintf(fp, "<a href=\"#%s\">%s", el->name.c_str(), el->name.c_str());
|
|
if (!el->shortName.empty())
|
|
fprintf(fp, " (%s)", el->shortName.c_str());
|
|
}
|
|
fputs("</a>", fp);
|
|
|
|
if (!el->description.empty())
|
|
fprintf(fp, " - %s\n", el->description.c_str());
|
|
|
|
fputs("</dd>\n", fp);
|
|
}
|
|
|
|
void OutputToc(FILE* fp, const ScriptingDocumentation& doc)
|
|
{
|
|
//***TOC: Libraries
|
|
fputs("<dt><b>Libraries</b></dt>\n", fp);
|
|
for (const auto& el : doc.elements) {
|
|
if (el->type != ElementType::Library) {
|
|
continue;
|
|
}
|
|
|
|
ade_output_toc(fp, el);
|
|
}
|
|
|
|
//***TOC: Objects
|
|
fputs("<dt><b>Types</b></dt>\n", fp);
|
|
for (const auto& el : doc.elements) {
|
|
if (el->type != ElementType::Class) {
|
|
continue;
|
|
}
|
|
|
|
ade_output_toc(fp, el);
|
|
}
|
|
|
|
//***TOC: Enumerations
|
|
fputs("<dt><b><a href=\"#Enumerations\">Enumerations</a></b></dt>", fp);
|
|
|
|
//***End TOC
|
|
fputs("</dl><br/><br/>", fp);
|
|
}
|
|
|
|
SCP_string element_name(const std::unique_ptr<DocumentationElement>& el)
|
|
{
|
|
if (!el->name.empty()) {
|
|
return el->name;
|
|
}
|
|
|
|
return el->shortName;
|
|
}
|
|
|
|
void ade_output_type_link(FILE* fp, const ade_type_info& type_info)
|
|
{
|
|
switch (type_info.getType()) {
|
|
case ade_type_info_type::Empty:
|
|
fputs("nothing", fp);
|
|
break;
|
|
case ade_type_info_type::Simple:
|
|
if (ade_is_internal_type(type_info.getIdentifier())) {
|
|
fputs(type_info.getIdentifier(), fp);
|
|
} else {
|
|
fprintf(fp, "<a href=\"#%s\">%s</a>", type_info.getIdentifier(), type_info.getIdentifier());
|
|
}
|
|
break;
|
|
case ade_type_info_type::Tuple: {
|
|
bool first = true;
|
|
for (const auto& type : type_info.elements()) {
|
|
if (!first) {
|
|
fprintf(fp, ", ");
|
|
}
|
|
first = false;
|
|
|
|
ade_output_type_link(fp, type);
|
|
}
|
|
break;
|
|
}
|
|
case ade_type_info_type::Array:
|
|
fputs("{ ", fp);
|
|
ade_output_type_link(fp, type_info.arrayType());
|
|
fputs(" ... }", fp);
|
|
break;
|
|
case ade_type_info_type::Map:
|
|
fputs("{ ", fp);
|
|
ade_output_type_link(fp, type_info.elements()[0]);
|
|
fputs(" -> ", fp);
|
|
ade_output_type_link(fp, type_info.elements()[1]);
|
|
fputs(" ... }", fp);
|
|
break;
|
|
case ade_type_info_type::Iterator:
|
|
fputs("iterator< ", fp);
|
|
ade_output_type_link(fp, type_info.arrayType());
|
|
fputs(" >", fp);
|
|
break;
|
|
case ade_type_info_type::Alternative: {
|
|
bool first = true;
|
|
for (const auto& type : type_info.elements()) {
|
|
if (!first) {
|
|
fputs(" | ", fp);
|
|
}
|
|
first = false;
|
|
|
|
ade_output_type_link(fp, type);
|
|
}
|
|
break;
|
|
}
|
|
case ade_type_info_type::Function: {
|
|
const auto& elements = type_info.elements();
|
|
if (elements.size() == 1) {
|
|
fputs("function() -> ", fp);
|
|
} else {
|
|
fputs("function(", fp);
|
|
bool first = true;
|
|
for (auto iter = elements.begin() + 1; iter != elements.end(); ++iter) {
|
|
if (!first) {
|
|
fputs(", ", fp);
|
|
}
|
|
first = false;
|
|
|
|
ade_output_type_link(fp, *iter);
|
|
|
|
fprintf(fp, " %s", iter->getName().c_str());
|
|
}
|
|
fputs(") -> ", fp);
|
|
}
|
|
ade_output_type_link(fp, elements.front());
|
|
break;
|
|
}
|
|
case ade_type_info_type::Generic: {
|
|
const auto& elements = type_info.elements();
|
|
ade_output_type_link(fp, elements.front());
|
|
if (elements.size() > 1) {
|
|
fputs("<", fp);
|
|
bool first = true;
|
|
for (auto iter = elements.begin() + 1; iter != elements.end(); ++iter) {
|
|
if (!first) {
|
|
fputs(", ", fp);
|
|
}
|
|
first = false;
|
|
|
|
ade_output_type_link(fp, *iter);
|
|
}
|
|
fputs(">", fp);
|
|
}
|
|
break;
|
|
}
|
|
case ade_type_info_type::Varargs: {
|
|
ade_output_type_link(fp, type_info.elements().front());
|
|
fputs("...", fp);
|
|
break;
|
|
}
|
|
default:
|
|
Assertion(false, "Unhandled type!");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void output_argument_list(FILE* fp, const DocumentationElementFunction::argument_list& overload)
|
|
{
|
|
if (overload.simple.empty()) {
|
|
bool first = true;
|
|
bool optional = false;
|
|
for (const auto& arg : overload.arguments) {
|
|
if (!first) {
|
|
fputs(", ", fp);
|
|
}
|
|
first = false;
|
|
|
|
if (arg.optional && !optional) {
|
|
fputs("[", fp);
|
|
optional = true;
|
|
}
|
|
|
|
ade_output_type_link(fp, arg.type);
|
|
|
|
if (!arg.name.empty()) {
|
|
fprintf(fp, " <i>%s</i>", arg.name.c_str());
|
|
|
|
if (!arg.def_val.empty()) {
|
|
fprintf(fp, " = <i>%s</i>", arg.def_val.c_str());
|
|
}
|
|
}
|
|
|
|
if (!arg.comment.empty()) {
|
|
fprintf(fp, " (%s)", arg.comment.c_str());
|
|
}
|
|
}
|
|
|
|
if (optional) {
|
|
fputs("]", fp);
|
|
}
|
|
} else {
|
|
fprintf(fp, "<i>%s</i>", overload.simple.c_str());
|
|
}
|
|
}
|
|
|
|
void OutputElement(FILE* fp,
|
|
const std::unique_ptr<DocumentationElement>& el,
|
|
const DocumentationElement* parent,
|
|
const DocumentationElement* parentParent)
|
|
{
|
|
if (el->name.empty() && el->shortName.empty()) {
|
|
Warning(LOCATION, "Found ade_table_entry with no name or shortname");
|
|
return;
|
|
}
|
|
|
|
bool skip_this = false;
|
|
|
|
// WMC - Hack
|
|
if (parent != nullptr) {
|
|
for (const auto& child : el->children) {
|
|
if (child->name == "__indexer") {
|
|
OutputElement(fp, child, el.get(), parent);
|
|
skip_this = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//***Begin entry
|
|
if (!skip_this) {
|
|
fputs("<dd><dl>", fp);
|
|
|
|
switch (el->type) {
|
|
case ElementType::Library:
|
|
case ElementType::Class: {
|
|
//***Name (ShortName)
|
|
if (parent == nullptr) {
|
|
fprintf(fp, "<dt id=\"%s\">", element_name(el).c_str());
|
|
}
|
|
if (el->name.empty()) {
|
|
if (el->type == ElementType::Class) {
|
|
const auto classEl = static_cast<DocumentationElementClass*>(el.get());
|
|
if (classEl->superClass.empty())
|
|
fprintf(fp, "<h2>%s</h2>\n", classEl->shortName.c_str());
|
|
else {
|
|
fprintf(fp,
|
|
"<h2>%s:<a href=\"#%s\">%s</a></h2>\n",
|
|
classEl->superClass.c_str(),
|
|
classEl->superClass.c_str(),
|
|
classEl->superClass.c_str());
|
|
}
|
|
} else {
|
|
fprintf(fp, "<h2>%s</h2>\n", el->shortName.c_str());
|
|
}
|
|
} else {
|
|
fprintf(fp, "<h2>%s", el->name.c_str());
|
|
|
|
if (!el->shortName.empty())
|
|
fprintf(fp, " (%s)", el->shortName.c_str());
|
|
|
|
if (el->type == ElementType::Class) {
|
|
const auto classEl = static_cast<DocumentationElementClass*>(el.get());
|
|
|
|
if (!classEl->superClass.empty()) {
|
|
fprintf(fp,
|
|
":<a href=\"#%s\">%s</a>",
|
|
classEl->superClass.c_str(),
|
|
classEl->superClass.c_str());
|
|
}
|
|
}
|
|
|
|
fputs("</h2>\n", fp);
|
|
}
|
|
fputs("</dt>\n", fp);
|
|
|
|
//***Description
|
|
if (!el->description.empty()) {
|
|
fprintf(fp, "<dd>%s</dd>\n", el->description.c_str());
|
|
}
|
|
break;
|
|
}
|
|
case ElementType::Operator:
|
|
case ElementType::Function: {
|
|
const auto funcEl = static_cast<DocumentationElementFunction*>(el.get());
|
|
|
|
//***Name(ShortName)(Arguments)
|
|
for (const auto& overload : funcEl->overloads) {
|
|
fputs("<dt>", fp);
|
|
|
|
fputs("<i>", fp);
|
|
if (!funcEl->returnType.isEmpty())
|
|
ade_output_type_link(fp, funcEl->returnType);
|
|
else
|
|
fputs("nil", fp);
|
|
fputs(" </i>", fp);
|
|
|
|
const string_conv* op = nullptr;
|
|
if (el->name.empty()) {
|
|
fprintf(fp, "<b>%s", el->shortName.c_str());
|
|
} else {
|
|
op = ade_get_operator(el->name.c_str());
|
|
|
|
// WMC - Do we have an operator?
|
|
if (op == nullptr) {
|
|
fprintf(fp, "<b>%s", el->name.c_str());
|
|
if (!el->shortName.empty()) {
|
|
const auto op2 = ade_get_operator(el->shortName.c_str());
|
|
if (op2 == nullptr)
|
|
fprintf(fp, "(%s)", el->shortName.c_str());
|
|
else
|
|
fprintf(fp, "(%s)", op2->dest);
|
|
}
|
|
} else {
|
|
// WMC - Hack
|
|
if (parent != nullptr && !parent->name.empty() && parentParent != nullptr &&
|
|
el->name == "__indexer") {
|
|
fprintf(fp, "<b>%s%s", parent->name.c_str(), op->dest);
|
|
} else
|
|
fprintf(fp, "<b>%s", op->dest);
|
|
}
|
|
}
|
|
|
|
if (op == nullptr) {
|
|
fputs("(</b>", fp);
|
|
output_argument_list(fp, overload);
|
|
fputs("<b>)</b>\n", fp);
|
|
} else {
|
|
fputs("</b> ", fp);
|
|
output_argument_list(fp, overload);
|
|
}
|
|
fputs("</dt>\n", fp);
|
|
}
|
|
|
|
//***Description
|
|
if (!el->description.empty()) {
|
|
fprintf(fp, "<dd>%s</dd>\n", el->description.c_str());
|
|
}
|
|
|
|
if (el->deprecationVersion.isValid()) {
|
|
if (!el->deprecationMessage.empty()) {
|
|
fprintf(fp,
|
|
"<dd><b>Deprecated starting with version %d.%d.%d:</b> %s</dd>\n",
|
|
el->deprecationVersion.major,
|
|
el->deprecationVersion.minor,
|
|
el->deprecationVersion.build,
|
|
el->deprecationMessage.c_str());
|
|
} else {
|
|
fprintf(fp,
|
|
"<dd><b>Deprecated starting with version %d.%d.%d.</b></dd>\n",
|
|
el->deprecationVersion.major,
|
|
el->deprecationVersion.minor,
|
|
el->deprecationVersion.build);
|
|
}
|
|
}
|
|
|
|
//***Result: ReturnDescription
|
|
if (!funcEl->returnDocumentation.empty()) {
|
|
fprintf(fp, "<dd><b>Returns:</b> %s</dd>\n", funcEl->returnDocumentation.c_str());
|
|
} else {
|
|
fputs("<dd><b>Returns:</b> Nothing</dd>\n", fp);
|
|
}
|
|
break;
|
|
}
|
|
case ElementType::Property: {
|
|
const auto propEl = static_cast<DocumentationElementProperty*>(el.get());
|
|
|
|
//***Type Name(ShortName)
|
|
fputs("<dt>\n", fp);
|
|
fputs("<i>", fp);
|
|
ade_output_type_link(fp, propEl->getterType);
|
|
fputs("</i> ", fp);
|
|
|
|
if (propEl->name.empty()) {
|
|
fprintf(fp, "<b>%s</b>\n", propEl->shortName.c_str());
|
|
} else {
|
|
fprintf(fp, "<b>%s", propEl->name.c_str());
|
|
if (!propEl->shortName.empty())
|
|
fputs(propEl->shortName.c_str(), fp);
|
|
fputs("</b>\n", fp);
|
|
}
|
|
if (!propEl->setterType.isEmpty()) {
|
|
fprintf(fp, " = ");
|
|
ade_output_type_link(fp, propEl->setterType);
|
|
}
|
|
fputs("</dt>\n", fp);
|
|
|
|
//***Description
|
|
if (!propEl->description.empty())
|
|
fprintf(fp, "<dd>%s</dd>\n", propEl->description.c_str());
|
|
|
|
if (!propEl->returnDocumentation.empty())
|
|
fprintf(fp, "<dd><b>Value:</b> %s</b></dd>\n", propEl->returnDocumentation.c_str());
|
|
break;
|
|
}
|
|
default:
|
|
Warning(LOCATION, "Unknown type '%d' passed to ade_table_entry::OutputMeta", static_cast<int>(el->type));
|
|
break;
|
|
}
|
|
}
|
|
|
|
fputs("<dd><dl>\n", fp);
|
|
for (const auto& child : el->children) {
|
|
if (parent == nullptr || child->name != "__indexer")
|
|
OutputElement(fp, child, el.get(), parent);
|
|
}
|
|
fputs("</dl></dd>\n", fp);
|
|
|
|
if (!skip_this)
|
|
fputs("<br></dl></dd>\n", fp);
|
|
}
|
|
|
|
void OutputLuaMeta(FILE* fp, const ScriptingDocumentation& doc)
|
|
{
|
|
OutputToc(fp, doc);
|
|
|
|
SCP_vector<ade_table_entry*> table_entries;
|
|
|
|
//***Everything
|
|
fputs("<dl>\n", fp);
|
|
for (const auto& el : doc.elements) {
|
|
OutputElement(fp, el, nullptr, nullptr);
|
|
}
|
|
fputs("</dl>\n", fp);
|
|
}
|
|
|
|
template <typename Container>
|
|
static void output_hook_variable_list(FILE* fp, const Container& vars)
|
|
{
|
|
fputs("<dl>", fp);
|
|
for (const auto& param : vars) {
|
|
fputs("<dt>", fp);
|
|
ade_output_type_link(fp, param.type);
|
|
fprintf(fp, " <i>%s</i></dt>", param.name);
|
|
fprintf(fp, "<dd><b>Description:</b> %s</dt>", param.description);
|
|
}
|
|
fputs("</dl>", fp);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void output_html_doc(const ScriptingDocumentation& doc, const SCP_string& filename)
|
|
{
|
|
FILE* fp = fopen(filename.c_str(), "w");
|
|
|
|
if (fp == nullptr) {
|
|
return;
|
|
}
|
|
|
|
fprintf(fp,
|
|
"<html>\n<head>\n\t<title>Script Output - FSO v%s (%s)</title>\n</head>\n",
|
|
FS_VERSION_FULL,
|
|
doc.name.c_str());
|
|
fputs("<body>", fp);
|
|
fprintf(fp, "\t<h1>Script Output - FSO v%s (%s)</h1>\n", FS_VERSION_FULL, doc.name.c_str());
|
|
|
|
// Scripting languages links
|
|
fputs("<dl>", fp);
|
|
|
|
//***Hooks
|
|
fputs("<dt><h2>Conditional Hooks</h2></dt>", fp);
|
|
fputs("<dd><dl>", fp);
|
|
|
|
// Conditions
|
|
fputs("<dt><b>Conditions</b></dt>", fp);
|
|
for (const auto& conditions : doc.conditions) {
|
|
fprintf(fp, "<dd>%s</dd>", conditions.c_str());
|
|
}
|
|
|
|
// Actions
|
|
fputs("<dt><b>Actions</b></dt>", fp);
|
|
fputs("<dd><dl>", fp);
|
|
for (const auto& hook : doc.actions) {
|
|
fprintf(fp, "<dt><b>%s</b></dt>", hook.name.c_str());
|
|
if (!hook.description.empty()) {
|
|
fprintf(fp, "<dd><b>Description:</b> %s", hook.description.c_str());
|
|
// Only write this for hooks with descriptions since this information is useless for legacy hooks
|
|
if (hook.overridable) {
|
|
fputs("<br><i>This hook is overridable</i>", fp);
|
|
} else {
|
|
fputs("<br><i>This hook is <b>not</b> overridable</i>", fp);
|
|
}
|
|
if (!hook.parameters.empty()) {
|
|
fputs("<br><b>Hook Variables:</b>", fp);
|
|
output_hook_variable_list(fp, hook.parameters);
|
|
}
|
|
fputs("</dd>", fp);
|
|
}
|
|
}
|
|
fputs("</dl></dd>", fp);
|
|
|
|
fputs("<dt><b>Global Hook Variables:</b> (set globally independent of a specific hook)</dt>", fp);
|
|
output_hook_variable_list(fp, doc.globalVariables);
|
|
fputs("</dl></dd><br />", fp);
|
|
|
|
// Languages
|
|
fputs("<dl>", fp);
|
|
//***Version info
|
|
fprintf(fp, "<dd>Lua Version: %s</dd>\n", LUA_RELEASE);
|
|
|
|
fputs("<dd>", fp);
|
|
|
|
OutputLuaMeta(fp, doc);
|
|
|
|
fputs("</dd>", fp);
|
|
|
|
//***Enumerations
|
|
fprintf(fp, "<dt id=\"Enumerations\"><h2>Enumerations</h2></dt>");
|
|
for (const auto& enumeration : doc.enumerations) {
|
|
// Cyborg17 -- Omit the deprecated flag
|
|
if (enumeration.name == "VM_EXTERNAL_CAMERA_LOCKED") {
|
|
continue;
|
|
}
|
|
// WMC - For now, print to the file without the description.
|
|
fprintf(fp, "<dd><b>%s</b></dd>", enumeration.name.c_str());
|
|
}
|
|
fputs("</dl></body></html>", fp);
|
|
|
|
fclose(fp);
|
|
}
|
|
|
|
} // namespace scripting
|