il2cpp-dump-analyzer-mcp
Version:
Agentic RAG system for analyzing IL2CPP dump.cs files from Unity games
375 lines • 14.5 kB
JavaScript
"use strict";
/**
* Utility functions for IL2CPP code generation
* Provides common functionality for naming conventions, formatting, and validation
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.IL2CPPTypeResolver = exports.CodeGenerationUtils = void 0;
const types_1 = require("./types");
/**
* Implementation of GenerationUtils interface
*/
class CodeGenerationUtils {
/**
* Convert string to specified naming convention
*/
toNamingConvention(str, convention) {
// Handle camelCase and PascalCase input by splitting on capital letters
const splitOnCaps = str.replace(/([a-z])([A-Z])/g, '$1 $2');
// Clean the input string and split into words
const cleaned = splitOnCaps.replace(/[^a-zA-Z0-9]/g, ' ').trim();
const words = cleaned.split(/\s+/).filter(word => word.length > 0);
if (words.length === 0)
return str;
switch (convention) {
case types_1.FileNamingConvention.PASCAL_CASE:
return words.map(word => this.capitalize(word)).join('');
case types_1.FileNamingConvention.CAMEL_CASE:
return words[0].toLowerCase() + words.slice(1).map(word => this.capitalize(word)).join('');
case types_1.FileNamingConvention.SNAKE_CASE:
return words.map(word => word.toLowerCase()).join('_');
case types_1.FileNamingConvention.KEBAB_CASE:
return words.map(word => word.toLowerCase()).join('-');
default:
return str;
}
}
/**
* Generate XML documentation comment
*/
generateXmlDoc(description, parameters, returns) {
let xmlDoc = '/// <summary>\n';
xmlDoc += `/// ${description}\n`;
xmlDoc += '/// </summary>\n';
if (parameters && parameters.length > 0) {
for (const param of parameters) {
xmlDoc += `/// <param name="${param}">Parameter description</param>\n`;
}
}
if (returns) {
xmlDoc += `/// <returns>${returns}</returns>\n`;
}
return xmlDoc;
}
/**
* Format code according to style options
*/
formatCode(code, style) {
let formatted = code;
// Handle line endings
formatted = formatted.replace(/\r?\n/g, style.lineEnding);
// Handle indentation
const lines = formatted.split(style.lineEnding);
let indentLevel = 0;
const indentString = style.indentation === 'spaces'
? ' '.repeat(style.indentSize)
: '\t'.repeat(style.indentSize);
const formattedLines = lines.map(line => {
const trimmed = line.trim();
// Decrease indent for closing braces
if (trimmed.startsWith('}')) {
indentLevel = Math.max(0, indentLevel - 1);
}
// Apply indentation
const indentedLine = trimmed.length > 0
? indentString.repeat(indentLevel) + trimmed
: '';
// Increase indent for opening braces
if (trimmed.endsWith('{')) {
indentLevel++;
}
return indentedLine;
});
formatted = formattedLines.join(style.lineEnding);
// Handle brace style
if (style.braceStyle === 'new_line') {
formatted = formatted.replace(/\s*{\s*/g, style.lineEnding + '{' + style.lineEnding);
}
else {
formatted = formatted.replace(/\s*{\s*/g, ' {' + style.lineEnding);
}
// Handle line length (basic implementation)
if (style.maxLineLength > 0) {
const wrappedLines = formatted.split(style.lineEnding).map(line => {
if (line.length <= style.maxLineLength) {
return line;
}
// Simple line wrapping - could be enhanced
return this.wrapLine(line, style.maxLineLength, indentString);
});
formatted = wrappedLines.join(style.lineEnding);
}
return formatted;
}
/**
* Validate generated C# code syntax (basic implementation)
*/
validateCSharpSyntax(code) {
const errors = [];
const warnings = [];
const lines = code.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const lineNumber = i + 1;
// Check for basic syntax issues
this.checkBraceBalance(line, lineNumber, errors);
this.checkSemicolons(line, lineNumber, errors);
this.checkKeywords(line, lineNumber, warnings);
this.checkNamingConventions(line, lineNumber, warnings);
}
// Check overall brace balance
this.checkOverallBraceBalance(code, errors);
return {
isValid: errors.length === 0,
errors,
warnings
};
}
/**
* Capitalize first letter of a word
*/
capitalize(word) {
if (word.length === 0)
return word;
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
}
/**
* Wrap long lines
*/
wrapLine(line, maxLength, indentString) {
if (line.length <= maxLength)
return line;
// Find good break points (after commas, before operators, etc.)
const breakPoints = [',', '&&', '||', '+', '-', '*', '/', '='];
let bestBreak = -1;
for (let i = maxLength - 1; i >= maxLength / 2; i--) {
const char = line.charAt(i);
if (breakPoints.includes(char)) {
bestBreak = i + 1;
break;
}
}
if (bestBreak === -1) {
bestBreak = maxLength;
}
const firstPart = line.substring(0, bestBreak).trimEnd();
const secondPart = line.substring(bestBreak).trimStart();
if (secondPart.length === 0) {
return firstPart;
}
return firstPart + '\n' + indentString + secondPart;
}
/**
* Check brace balance in a line
*/
checkBraceBalance(line, lineNumber, errors) {
const openBraces = (line.match(/{/g) || []).length;
const closeBraces = (line.match(/}/g) || []).length;
// This is a simple check - more sophisticated parsing would be needed for production
if (openBraces > 1 || closeBraces > 1) {
errors.push({
message: 'Multiple braces on single line may indicate syntax error',
line: lineNumber,
column: line.indexOf('{') !== -1 ? line.indexOf('{') + 1 : line.indexOf('}') + 1,
severity: 'warning'
});
}
}
/**
* Check for missing semicolons
*/
checkSemicolons(line, lineNumber, errors) {
const trimmed = line.trim();
// Lines that should end with semicolon
const shouldEndWithSemicolon = /^(var|int|string|bool|float|double|decimal|public|private|protected|internal)\s+.*[^{};]$/.test(trimmed);
if (shouldEndWithSemicolon && !trimmed.endsWith(';') && !trimmed.endsWith('{')) {
errors.push({
message: 'Statement may be missing semicolon',
line: lineNumber,
column: trimmed.length,
severity: 'error'
});
}
}
/**
* Check for C# keyword usage
*/
checkKeywords(line, lineNumber, warnings) {
const csharpKeywords = [
'abstract', 'as', 'base', 'bool', 'break', 'byte', 'case', 'catch', 'char', 'checked',
'class', 'const', 'continue', 'decimal', 'default', 'delegate', 'do', 'double', 'else',
'enum', 'event', 'explicit', 'extern', 'false', 'finally', 'fixed', 'float', 'for',
'foreach', 'goto', 'if', 'implicit', 'in', 'int', 'interface', 'internal', 'is',
'lock', 'long', 'namespace', 'new', 'null', 'object', 'operator', 'out', 'override',
'params', 'private', 'protected', 'public', 'readonly', 'ref', 'return', 'sbyte',
'sealed', 'short', 'sizeof', 'stackalloc', 'static', 'string', 'struct', 'switch',
'this', 'throw', 'true', 'try', 'typeof', 'uint', 'ulong', 'unchecked', 'unsafe',
'ushort', 'using', 'virtual', 'void', 'volatile', 'while'
];
// Check for potential keyword misuse (basic check)
const words = line.split(/\s+/);
for (const word of words) {
const cleanWord = word.replace(/[^a-zA-Z]/g, '');
if (csharpKeywords.includes(cleanWord.toLowerCase()) && cleanWord !== cleanWord.toLowerCase()) {
warnings.push(`Line ${lineNumber}: Potential keyword casing issue: ${cleanWord}`);
}
}
}
/**
* Check naming conventions
*/
checkNamingConventions(line, lineNumber, warnings) {
// Check for PascalCase class names
const classMatch = line.match(/class\s+([a-zA-Z_][a-zA-Z0-9_]*)/);
if (classMatch) {
const className = classMatch[1];
if (!/^[A-Z][a-zA-Z0-9]*$/.test(className)) {
warnings.push(`Line ${lineNumber}: Class name '${className}' should use PascalCase`);
}
}
// Check for camelCase method names
const methodMatch = line.match(/(public|private|protected|internal)\s+.*\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/);
if (methodMatch) {
const methodName = methodMatch[2];
if (!/^[A-Z][a-zA-Z0-9]*$/.test(methodName)) {
warnings.push(`Line ${lineNumber}: Method name '${methodName}' should use PascalCase`);
}
}
}
/**
* Check overall brace balance in code
*/
checkOverallBraceBalance(code, errors) {
let braceCount = 0;
let lineNumber = 1;
for (const char of code) {
if (char === '{') {
braceCount++;
}
else if (char === '}') {
braceCount--;
if (braceCount < 0) {
errors.push({
message: 'Unmatched closing brace',
line: lineNumber,
column: 1,
severity: 'error'
});
return;
}
}
else if (char === '\n') {
lineNumber++;
}
}
if (braceCount > 0) {
errors.push({
message: `${braceCount} unmatched opening brace(s)`,
line: lineNumber,
column: 1,
severity: 'error'
});
}
}
}
exports.CodeGenerationUtils = CodeGenerationUtils;
/**
* Implementation of TypeResolver interface for IL2CPP types
*/
class IL2CPPTypeResolver {
constructor() {
this.typeMap = new Map();
this.unityTypes = new Set();
this.initializeTypeMappings();
this.initializeUnityTypes();
}
/**
* Resolve IL2CPP type to C# type
*/
resolveType(il2cppType) {
// Handle generic types
if (il2cppType.includes('<') && il2cppType.includes('>')) {
return this.resolveGenericType(il2cppType, []);
}
// Handle array types
if (il2cppType.endsWith('[]')) {
const baseType = il2cppType.slice(0, -2);
return `${this.resolveType(baseType)}[]`;
}
// Direct mapping
return this.typeMap.get(il2cppType) || il2cppType;
}
/**
* Check if type is Unity-specific
*/
isUnityType(type) {
return this.unityTypes.has(type) || type.startsWith('Unity.');
}
/**
* Get using statements for type
*/
getUsingsForType(type) {
const usings = [];
if (this.isUnityType(type)) {
usings.push('using UnityEngine;');
}
if (type.includes('System.')) {
usings.push('using System;');
}
if (type.includes('Collections')) {
usings.push('using System.Collections.Generic;');
}
return [...new Set(usings)]; // Remove duplicates
}
/**
* Resolve generic type parameters
*/
resolveGenericType(type, genericArgs) {
// Basic generic type resolution
const baseType = type.split('<')[0];
const resolvedBase = this.resolveType(baseType);
if (genericArgs.length > 0) {
const resolvedArgs = genericArgs.map(arg => this.resolveType(arg));
return `${resolvedBase}<${resolvedArgs.join(', ')}>`;
}
return type;
}
/**
* Initialize IL2CPP to C# type mappings
*/
initializeTypeMappings() {
// Basic type mappings
this.typeMap.set('System.Void', 'void');
this.typeMap.set('System.Boolean', 'bool');
this.typeMap.set('System.Byte', 'byte');
this.typeMap.set('System.SByte', 'sbyte');
this.typeMap.set('System.Int16', 'short');
this.typeMap.set('System.UInt16', 'ushort');
this.typeMap.set('System.Int32', 'int');
this.typeMap.set('System.UInt32', 'uint');
this.typeMap.set('System.Int64', 'long');
this.typeMap.set('System.UInt64', 'ulong');
this.typeMap.set('System.Single', 'float');
this.typeMap.set('System.Double', 'double');
this.typeMap.set('System.Decimal', 'decimal');
this.typeMap.set('System.Char', 'char');
this.typeMap.set('System.String', 'string');
this.typeMap.set('System.Object', 'object');
}
/**
* Initialize Unity-specific types
*/
initializeUnityTypes() {
const unityTypes = [
'GameObject', 'Transform', 'Component', 'MonoBehaviour', 'ScriptableObject',
'Vector2', 'Vector3', 'Vector4', 'Quaternion', 'Color', 'Rect', 'Bounds',
'Camera', 'Light', 'Renderer', 'MeshRenderer', 'SkinnedMeshRenderer',
'Collider', 'BoxCollider', 'SphereCollider', 'CapsuleCollider', 'MeshCollider',
'Rigidbody', 'Rigidbody2D', 'AudioSource', 'AudioClip', 'Texture', 'Texture2D',
'Material', 'Shader', 'Mesh', 'Animation', 'Animator', 'AnimationClip'
];
unityTypes.forEach(type => this.unityTypes.add(type));
}
}
exports.IL2CPPTypeResolver = IL2CPPTypeResolver;
//# sourceMappingURL=utils.js.map