/* RetroArch - A frontend for libretro. * Copyright (C) 2014-2018 - Ali Bouhlel * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with RetroArch. * If not, see . */ #include #include #include #include #include using namespace peg; using namespace std; template AstBase AstBase::empty = AstBase("", 0, 0, "", string("")); //bool use_typedefs = false; bool use_typedefs = true; //const char* prefix_seperator = "_"; const char* prefix_seperator = ""; vector ignored_fn_prefixes_list = { "OM", "IA", "RS", }; vector fn_prefixes_list = { "OM", "IA", "RS", "VS", "PS", "GS", "DS", "CS", }; bool move_fn_prefix_after_action = true; vector ignored_types_list = { "AsyncIUnknown", "IMarshal", "IMalloc", "IEnumString", "ISequentialStream", "IStream", "IRpcChannelBuffer", "IAsyncRpcChannelBuffer", "IRpcProxyBuffer", "IServerSecurity", "IRpcOptions", "IGlobalOptions", "ISurrogate", "ISynchronize", "ISynchronizeEvent", "IDataObject", "IDataAdviseHolder", "IDirectWriterLock", "IDummyHICONIncluder", "IDispatch", "IDropSource", "IDropTarget", "IDataFilter", "IDropSourceNotify", }; const char* required_prefix = "ID"; vector ignored_functions_list = { "Release", "QueryInterface", "AddRef", "GetParent", "GetAdapter", "GetDevice", "GetDesc", "GetType", "SetName", "SetPrivateDataInterface", "SetPrivateData", "GetPrivateData", "OpenSharedHandle", "CreateSharedHandle", "OpenSharedHandleByName", "SetTrackingOptions", }; vector overloaded_list = { "Release", "QueryInterface", "AddRef", "GetParent", "GetAdapter", "GetDesc", "GetType", "SetName", "SetPrivateDataInterface", "SetPrivateData", "GetPrivateData", "Map", "Unmap", "Reset", "Signal", "BeginEvent", "EndEvent", "SetMarker", "AssertResourceState", "GetFeatureMask", "SetFeatureMask", "GetFrameStatistics", "Close", "SetEvictionPriority", "GetEvictionPriority", "GetResource", "GetDataSize", "GetContextFlags", "GetCertificate", "GetCertificateSize", "Begin", "End", "GetData", "CopySubresourceRegion", "CreateRenderTargetView", "CreateShaderResourceView", }; vector action_list = { "Release", "Get", "Set", "Enum", "Get", "Query", "Add", "Signal", "Begin", "End", "Assert", "Close", "Map", "Unmap" }; vector derived_types_list = { "ID3D12Device", "ID3D12Debug", "ID3D12DebugDevice", "ID3D12DebugCommandList", "IDXGIFactory1", "IDXGIAdapter1", "IDXGISurface1", "IDXGISwapChain3", "IDXGIOutput", "IDXGIDevice", }; vector base_objects_list = { "IUnknown", "ID3D12Object", // "ID3D12Resource", "IDXGIObject", "IDXGIResource", // "ID3D11Resource", // "ID3D10Resource", }; //string insert_name(const char* fname, const char* name) string insert_name(const string& fname, const string& name) { string str; for(string action: action_list) { if(!strncmp(fname.c_str(), action.c_str(), action.length())) { if(name.length() == 2 && name[1] == 'S') { if(!strncmp(fname.c_str() + action.length(), "Shader", strlen("Shader"))) return action + name[0] + (fname.c_str() + action.length()); else return action + name[0] + "Shader" + (fname.c_str() + action.length()); } else return action + name + (fname.c_str() + action.length()); } } return string(fname) + name; } //string insert_name(string fname, string name) //{ // return insert_name(fname.c_str(), name.c_str()); //} string get_derived_basename(const char *name) { string str = name; if(::isdigit(str.back())) str.pop_back(); return str; } string get_derived_basename(string name) { return get_derived_basename(name.c_str()); } string get_prefix(const char *type) { string prefix; if(*type != 'I') return type; type++; while (::isalnum(*type) && !::islower(*type)) prefix.push_back(*type++); prefix.pop_back(); return prefix; } string get_prefix(const string &type) { return get_prefix(type.c_str()); } string clean_name(const char *name) { string str; if (name[0] == 'p' && name[1] == 'p' && name[2] == 'v' && ::isupper(name[3])) name += 3; if (name[0] == 'p' && name[1] == 'p' && ::isupper(name[2])) name += 2; else if ((*name == 'p' || *name == 'b') && ::isupper(name[1])) name ++; bool was_upper = false; if (*name) { if (::isupper(*name)) { was_upper = true; str = (char)::tolower(*name); } else str = *name; } while (*++name) { if (::isupper(*name)) { if(!was_upper && !::isdigit(str.back())) str = str + '_'; was_upper = true; str += (char)::tolower(*name); } else { was_upper = false; str += *name; } } if(str == "enum") str = "enumerator"; return str; } struct name_pair{ string name; string original; }; class ComFunction { public: struct param_t { name_pair type; name_pair ptr; name_pair name; int array_size = 0; bool const_ = false; bool base = false; bool derived = false; }; param_t this_; vector params; string riid; string name; string original_name; string return_type; string prefix; bool overloaded = false; bool ignored = false; bool preferred = false; ComFunction(Ast node) { original_name = node["FunctionName"]; return_type = node["ReturnType"]; for (auto param_ : node["Parameters"].nodes) { Ast ¶m = *param_; param_t p; p.type.original = param["Type"]["Name"]; p.const_ = param["Type"]["TypeQualifier"] == "const"; if(p.type.original == "REFIID") { riid = string("I") + prefix; continue; } if(!riid.empty()) { if(param["Name"][2] == 'v') riid += param["Name"] + 3; else riid += param["Name"] + 2; break; } p.ptr.original = param["Type"]["Pointer"]; p.ptr.name = p.ptr.original; if(use_typedefs && (prefix.empty() || (p.type.original[0] == 'I' && !strncmp(p.type.original.c_str() + 1, prefix.c_str(), prefix.length()))) ) { p.type.name = p.type.original.c_str() + 1; if(::isdigit(p.type.name.back())) p.type.name.pop_back(); if(!p.ptr.name.empty()) p.ptr.name.pop_back(); } else { p.type.name = p.type.original; p.ptr.name = p.ptr.name; } if (param.name == "This") { prefix = get_prefix(p.type.original); p.name.original = param["Type"]["Name"] + 1 + prefix.length(); p.name.name = clean_name(p.name.original.c_str()); if(::isdigit(p.name.name.back())) p.name.name.pop_back(); this_ = p; } else { p.name.original = param["Name"]; p.name.name = clean_name(p.name.original.c_str()); p.array_size = ::strtoul(param["ArraySize"], NULL, 10); params.push_back(p); } } if(original_name == "GetCPUDescriptorHandleForHeapStart") { param_t p; p.type.name = p.type.original = return_type; p.ptr.name = p.ptr.original = "*"; return_type = "void"; p.name.name = p.name.original = "out"; params.push_back(p); } for(string str : ignored_functions_list) if(original_name == str) ignored = true; for(string str : ignored_types_list) if(get_derived_basename(this_.type.original) == get_derived_basename(str)) ignored = true; if(required_prefix) if(strncmp(this_.type.original.c_str(), required_prefix, strlen(required_prefix))) ignored = true; for(string str : derived_types_list) { if(get_derived_basename(this_.type.original) == get_derived_basename(str)) { this_.derived = true; if(this_.type.original != str) ignored = true; } for (param_t ¶m : params) if(get_derived_basename(param.type.original) == get_derived_basename(str)) { if(param.type.original != str) param.derived = true; else { // cout << param.type.original << endl; preferred = true; } } } for(string str : overloaded_list) if(original_name == str) overloaded = true; for(string str : base_objects_list) { if(this_.type.original == str) this_.base = true; for (param_t ¶m : params) if(param.type.original == str) param.base = true; } name = prefix + prefix_seperator; if(overloaded && !this_.base) { name += insert_name(original_name, this_.name.original); if(::isdigit(name.back())) name.pop_back(); } else { string fn_prefix; for (string& str : ignored_fn_prefixes_list) if(!strncmp(original_name.c_str(), str.c_str(), str.length())) { if(::isupper(original_name[str.length()])) fn_prefix = str; break; } if(!fn_prefix.empty()) { name += original_name.c_str() + fn_prefix.length(); } else { for (string str : fn_prefixes_list) if(!strncmp(original_name.c_str(), str.c_str(), str.length())) { if(::isupper(original_name[str.length()])) fn_prefix = str; break; } if(!fn_prefix.empty()) name += insert_name(original_name.c_str() + fn_prefix.length(), fn_prefix.c_str()); else name += original_name; } } // if(original_name == "CreateCommandQueue" && ignored) // cout << "ignored CreateCommandQueue !!" << __LINE__ << endl; } string str() { stringstream out; out << "static inline " << return_type << " " << name << "("; if(this_.base) out << "void*"; else out << this_.type.name << this_.ptr.name; out << " " << this_.name.name; for (param_t ¶m : params) { out << ", "; if(param.const_) out << "const "; if(param.base) out << "void*"; else out << param.type.name << param.ptr.name; out << " " << param.name.name; if(param.array_size) out << '[' << param.array_size << ']'; } if(!riid.empty()) out << ", " << riid << "** out"; out << ")\n{\n"; out << " "; if (return_type != "void") out << "return "; if(original_name == "GetCPUDescriptorHandleForHeapStart") out << "((void (STDMETHODCALLTYPE *)(ID3D12DescriptorHeap*, D3D12_CPU_DESCRIPTOR_HANDLE*))\n "; if(this_.base) out << "((" << this_.type.original << this_.ptr.original << ')' << this_.name.name << ')'; else out << this_.name.name; out << "->lpVtbl->" << original_name; if(original_name == "GetCPUDescriptorHandleForHeapStart") out << ')'; out << '(' << this_.name.name; for (param_t param : params) { out << ", "; if(param.base || param.derived) out << '(' << param.type.original << param.ptr.original << ')'; out << param.name.name; } if(!riid.empty()) out << ", uuidof(" << riid << "), (void**)out"; out << ");\n"; out << "}\n"; return out.str(); } }; class ComHeader { public: shared_ptr ast; vector functions; vector types; ComHeader(const char *filename) { string header; { ifstream fs(filename); #if 0 stringstream ss; ss << fs.rdbuf(); header = ss.str(); #else while (!fs.eof()) { char line[4096]; fs.getline(line, sizeof(line)); char* str = line; while (*str && ::isspace(*str)) str++; if (*str && !strncmp(str, "typedef struct ", strlen("typedef struct "))) { if(*str && strstr(str, "Vtbl")) { header += str; header.push_back('\n'); while(*line) { fs.getline(line, sizeof(line)); str = line; while (*str && ::isspace(*str)) str++; if(*str) { header += str; header.push_back('\n'); } if(*str == '}') break; } } } } #endif } { ofstream out("test.cpp.h"); out << header; out.close(); } string header_grammar; { ifstream fs("grammar.txt"); stringstream ss; ss << fs.rdbuf(); header_grammar = ss.str(); } parser parser; parser.log = [&](size_t ln, size_t col, const string & msg) { cout << "Error parsing grammar:" << ln << ":" << col << ": " << msg << endl; }; if (!parser.load_grammar(header_grammar.c_str())) { cout << "Failed to load grammar" << endl; return; } parser.log = [&](size_t ln, size_t col, const string & msg) { cout << filename << ":" << ln << ":" << col << ": " << msg << endl; }; parser.enable_ast(); if (!parser.parse_n(header.c_str(), header.size(), ast)) { cout << "Error parsing header file: " << filename << endl; return; } ast = AstOptimizer(false).optimize(ast); if (ast->name != "Program") { cout << "Expected root node to be Program, not" << ast->name << endl; return; } for (shared_ptr node_ : ast->nodes) { Ast node = *node_; if (node.name == "Function") functions.push_back(node); else if (node.name == "Line") { } else if (node.name == "EndOfFile") break; else { // cout << "Unexcpected node " << node->name << endl; } } for(ComFunction& fn : functions) { if(!fn.ignored && fn.preferred && ::isdigit(fn.name.back())) { fn.name.pop_back(); for(ComFunction& fn2 : functions) if(&fn != &fn2 && !fn2.ignored && get_derived_basename(fn.original_name) == get_derived_basename(fn2.original_name)) { // cout << &fn << &fn2 << fn.original_name << " " << fn2.original_name << endl; // assert(fn2.preferred == false); fn2.ignored = true; // if(fn2.original_name == "CreateCommandQueue" && fn2.ignored) // cout << "ignored CreateCommandQueue !!" << __LINE__ << endl; } } } for(ComFunction& fn : functions) { if(!fn.ignored) { bool known = false; for(name_pair& known_type:types) if (fn.this_.type.original == known_type.original) { known = true; break; } if(!known) types.push_back(fn.this_.type); } } } ComHeader(shared_ptr ast_) { } string str() { stringstream out; int indent = 0; for(name_pair known_type:types) if (indent < known_type.original.length()) indent = known_type.original.length(); for(name_pair known_type:types) { out << "typedef " << known_type.original << '*' << string(indent + 1 - known_type.original.length(), ' ') << known_type.name << ";\n"; } out << "\n\n"; for (ComFunction &fn : functions) if(!fn.ignored) out << fn.str(); return out.str(); } }; template basic_ostream<_Elem, _Traits> &operator << (basic_ostream<_Elem, _Traits> &ios, ComHeader &header) { return ios << header.str(); } int main(int argc, const char **argv) { const char *header_fn = argv[1]; ComHeader header(header_fn); if (header.functions.empty()) return 1; if (argc > 2) cout << ast_to_s(header.ast) << flush; ofstream out("test.h"); const char* basename = strrchr(argv[1], '/'); if(!basename) basename = strrchr(argv[1], '\\'); if(basename) basename++; if(!basename) basename = argv[1]; #if 1 out << "\n#include <" << basename << ">\n\n"; #else out << "\n"; out << "#include \n"; out << "#include \n"; out << "#include \n\n"; #endif out << header; out.close(); return 0; }