const fs = require( "node:fs/promises" ); const path = require( "path" ); const { loadJSON, getFiles, saveUTF8, getMatches, loadUTF8, insertText } = require( "./library.js" ); const { CppClassMember } = require( "./cpp-class-member.js" ); function createCppHeader( className, extendingClassName, headerDefine, protectedMembers, publicMembers ) { let cppHeader = ` /* ${className}.h */ #ifndef ${headerDefine} #define ${headerDefine} #include "./${extendingClassName}.h" class ${className} : public ${extendingClassName} { GDCLASS(${className}, ${extendingClassName}); protected: static void _bind_methods(); ${protectedMembers} public: ${publicMembers} ${className}(); ~${className}(); }; #endif // ${headerDefine} ` return cppHeader; } function createCppImplementation( className, boundMethods ) { let cppImplementation = ` /* ${className}.cpp */ #include "${className}.h" void ${className}::_bind_methods() { ${boundMethods} } ${className}::${className}() { } ${className}::~${className}() { } ` return cppImplementation; } let definitionsPath = __dirname + "/rokojori-action-library-definitions"; let outputPath = __dirname + "/rokojori-action-library"; let registerTypesPath = outputPath + "/register_types.cpp"; let classRegistrationRegex = /ClassDB\:\:register_class\<(\w+)\>\(\)\;/; function createTypeRegistration( className ) { return `ClassDB::register_class<${className}>();`; } function createIncludes( className ) { return `#include "./${className}.h"`; } async function main() { let files = await getFiles( definitionsPath ); let typesFile = await loadUTF8( registerTypesPath ); let types = getRegistratedTypes( typesFile ); let missingTypes = []; for ( let file of files ) { await createCppFiles( file, types, missingTypes ); } if ( missingTypes.length === 0 ) { return; } console.log( "Registrating missing types:", missingTypes ); let additions = missingTypes.map( mt => createTypeRegistration( mt ) ); let additionsBlock = additions.join( "\n " ); let lastType = types[ types.length -1 ]; let insertPosition = lastType.index + lastType.match.length; let updatedTypesFile = insertText( typesFile, "\n " + additionsBlock, insertPosition ); let includeMatches = getMatches( updatedTypesFile, /#include "(\w|\.|\/|\\)+.h"/ ); let lastInclude = includeMatches[ includeMatches.length - 1 ]; let includes = missingTypes.map( mt => createIncludes( mt ) ).join( "\n" ); insertPosition = lastInclude.index + lastInclude.match.length; updatedTypesFile = insertText( updatedTypesFile, "\n" + includes, insertPosition ); saveUTF8( registerTypesPath, updatedTypesFile ); } function grabMembers( membersData ) { let members = []; if ( ! membersData ) { return members; } for ( let it in membersData ) { if ( ! membersData.hasOwnProperty( it ) ) { continue; } let nameDefinition = it; let memberData = membersData[ it ]; let classMember = new CppClassMember( nameDefinition, memberData ); members.push( classMember ); } return members; } function getHeaderDefine( className ) { className = className.replace( /^RJ/, "" ); let output = [ className[ 0 ] ]; for ( let i = 1; i < className.length; i++ ) { let character = className[ i ]; let upperCaseCharacter = character.toUpperCase(); if ( character === upperCaseCharacter ) { output.push( "_" ); } output.push( upperCaseCharacter ); } className = output.join( "" ); return "ROKOJORI_CORE__" + className + "_H"; } function getRegistratedTypes( text ) { let matches = getMatches( text, classRegistrationRegex ); let types = matches.map( m => { return { match: m.match, index: m.index, type: classRegistrationRegex.exec( m.match )[ 1 ] } } ); return types; } async function createCppFiles( definitionPath, types, missingTypes ) { console.log( "Creating: ", definitionPath ); let data = await loadJSON( path.join( definitionsPath, definitionPath ) ); let classNameResult = /(\w+)(?:\:(\w+))?/.exec( data.class ); let className = classNameResult[ 1 ]; let extendingClass = classNameResult[ 2 ]; let headerDefine = getHeaderDefine( className ); let protectedMembers = grabMembers( data.protected ); let publicMembers = grabMembers( data.public ); let protectedHeader = protectedMembers.map( p => p.getHeaderDefinition() ).join( "\n " ); let publicHeader = publicMembers.map( p => p.getHeaderDefinition() ).join( "\n " ); let protectedBindings = protectedMembers.map( p => p.getMethodBinding( className ) ); let publicBindings = publicMembers.map( p => p.getMethodBinding( className ) ); let bindings = protectedBindings.concat( publicBindings ).join( "\n " ); let header = createCppHeader( className, extendingClass, headerDefine, protectedHeader, publicHeader ); let implementation = createCppImplementation( className, bindings ); //console.log( header ); //console.log( implementation ); let hasType = types.find( t => t.type === className ); if ( ! hasType ) { missingTypes.push( className ); } let rawFilePath = path.join( outputPath, className ); await saveUTF8( rawFilePath + ".h", header ); await saveUTF8( rawFilePath + ".cpp", implementation ); } main();