UNPKG

il2cpp-dump-analyzer-mcp

Version:

Agentic RAG system for analyzing IL2CPP dump.cs files from Unity games

290 lines 11.9 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.IL2CPPIndexer = void 0; const path = __importStar(require("path")); const enhanced_il2cpp_parser_1 = require("../parser/enhanced-il2cpp-parser"); const chunker_1 = require("../embeddings/chunker"); const vector_store_1 = require("../embeddings/vector-store"); const hash_manager_factory_1 = require("../utils/hash-manager-factory"); /** * Manages the indexing process for IL2CPP dump files */ class IL2CPPIndexer { constructor(chunkSize = 1000, chunkOverlap = 200, model, hashFilePath) { this.chunkSize = chunkSize; this.chunkOverlap = chunkOverlap; this.model = model; this.parser = new enhanced_il2cpp_parser_1.EnhancedIL2CPPParser(); this.chunker = new chunker_1.IL2CPPCodeChunker(chunkSize, chunkOverlap); this.vectorStore = new vector_store_1.IL2CPPVectorStore(model); this.hashManager = (0, hash_manager_factory_1.createHashManagerFromEnv)(hashFilePath); } /** * Index an IL2CPP dump file * @param filePath Path to the dump.cs file * @param progressCallback Optional callback for progress updates * @param forceReprocess Force reprocessing even if file was already processed * @returns The vector store with indexed content */ async indexFile(filePath, progressCallback, forceReprocess = false) { // Initialize hash manager if it supports async initialization if (this.hashManager.initialize) { await this.hashManager.initialize(); } // Check if file was already processed (unless forced) let isProcessed = false; if (!forceReprocess) { if (this.hashManager.isFileProcessedAsync) { // Use async method for Supabase hash manager isProcessed = await this.hashManager.isFileProcessedAsync(filePath); } else { // Use sync method for local hash manager isProcessed = this.hashManager.isFileProcessed(filePath); } } if (isProcessed) { const hash = this.hashManager.getFileHash(filePath); const fileName = path.basename(filePath); if (progressCallback) { progressCallback(100, `Skipping duplicate file: ${fileName} (hash: ${hash.substring(0, 8)}...)`); } console.log(`Skipping already processed file: ${fileName} (hash: ${hash.substring(0, 8)}...)`); return this.vectorStore; } // Calculate and log file hash const fileHash = this.hashManager.getFileHash(filePath); const fileName = path.basename(filePath); console.log(`Processing file: ${fileName} (hash: ${fileHash.substring(0, 8)}...)`); if (progressCallback) { progressCallback(5, `Processing file: ${fileName} (hash: ${fileHash.substring(0, 8)}...)`); } // Load and parse the file await this.parser.loadFile(filePath); if (progressCallback) { progressCallback(10, 'File loaded. Extracting classes...'); } // Extract all entities using enhanced parser const result = this.parser.extractAllConstructs(); if (progressCallback) { progressCallback(30, `Extracted ${result.statistics.totalConstructs} constructs (${result.statistics.coveragePercentage.toFixed(1)}% coverage). Creating chunks...`); } const classes = result.classes; const enums = result.enums; const delegates = result.delegates; const generics = result.generics; const nestedTypes = result.nestedTypes; if (progressCallback) { progressCallback(40, `Processing ${classes.length} classes, ${enums.length} enums, ${delegates.length} delegates, ${generics.length} generics, ${nestedTypes.length} nested types...`); } // Create chunks const chunks = []; // Process classes in batches to avoid memory issues const classBatchSize = 100; for (let i = 0; i < classes.length; i += classBatchSize) { const batch = classes.slice(i, i + classBatchSize); const batchChunks = await this.processClassBatch(batch); chunks.push(...batchChunks); if (progressCallback) { const progress = 40 + Math.floor((i / classes.length) * 30); progressCallback(progress, `Processed ${i + batch.length}/${classes.length} classes...`); } } // Process enums for (const enumEntity of enums) { const enumChunks = await this.chunker.chunkEnum(enumEntity); chunks.push(...enumChunks); } // Process delegates as special classes for (const delegate of delegates) { const delegateAsClass = { name: delegate.name, namespace: delegate.namespace, fullName: delegate.fullName, baseClass: 'MulticastDelegate', interfaces: [], fields: [], methods: [delegate.invokeMethod, delegate.constructorMethod].filter(Boolean), isMonoBehaviour: false, typeDefIndex: delegate.typeDefIndex, attributes: delegate.attributes || [] }; const delegateChunks = await this.chunker.chunkClass(delegateAsClass); chunks.push(...delegateChunks); } // Process generics as enhanced classes for (const generic of generics) { const genericAsClass = { name: generic.name, namespace: generic.namespace, fullName: generic.fullName, baseClass: generic.baseClass, interfaces: generic.interfaces, fields: generic.fields, methods: generic.methods, isMonoBehaviour: false, typeDefIndex: generic.typeDefIndex, attributes: generic.attributes || [] }; const genericChunks = await this.chunker.chunkClass(genericAsClass); chunks.push(...genericChunks); } // Process nested types as classes for (const nested of nestedTypes) { const nestedAsClass = { name: nested.name, namespace: nested.namespace, fullName: nested.fullName, baseClass: nested.baseClass, interfaces: nested.interfaces, fields: nested.fields, methods: nested.methods, isMonoBehaviour: false, typeDefIndex: nested.typeDefIndex, attributes: nested.attributes || [] }; const nestedChunks = await this.chunker.chunkClass(nestedAsClass); chunks.push(...nestedChunks); } if (progressCallback) { progressCallback(70, `Created ${chunks.length} chunks. Adding to vector store...`); } // Add chunks to vector store await this.vectorStore.addCodeChunks(chunks); // Mark file as processed this.hashManager.markFileAsProcessed(filePath); if (progressCallback) { progressCallback(100, `Indexing complete! File hash ${fileHash.substring(0, 8)}... saved to skip future duplicates.`); } console.log(`File processed and hash saved: ${fileName} (${fileHash.substring(0, 8)}...)`); return this.vectorStore; } /** * Process a batch of classes to create chunks * @param classes Batch of classes to process * @returns Array of code chunks */ async processClassBatch(classes) { const chunks = []; for (const classEntity of classes) { // Ensure attributes is always an array for compatibility const compatibleClass = { ...classEntity, attributes: classEntity.attributes || [] }; const classChunks = await this.chunker.chunkClass(compatibleClass); chunks.push(...classChunks); } return chunks; } /** * Get the vector store instance * @returns Vector store instance */ getVectorStore() { return this.vectorStore; } /** * Get the hash manager instance * @returns Hash manager instance */ getHashManager() { return this.hashManager; } /** * Check if a file has been processed before * @param filePath Path to the dump.cs file * @returns true if file was already processed */ isFileProcessed(filePath) { return this.hashManager.isFileProcessed(filePath); } /** * Check if a file has been processed before (async version) * @param filePath Path to the dump.cs file * @returns Promise that resolves to true if file was already processed */ async isFileProcessedAsync(filePath) { // Initialize hash manager if it supports async initialization if (this.hashManager.initialize) { await this.hashManager.initialize(); } if (this.hashManager.isFileProcessedAsync) { return await this.hashManager.isFileProcessedAsync(filePath); } else { return this.hashManager.isFileProcessed(filePath); } } /** * Get the hash of a file * @param filePath Path to the dump.cs file * @returns SHA-256 hash of the file */ getFileHash(filePath) { return this.hashManager.getFileHash(filePath); } /** * Remove a file from the processed list (to allow reprocessing) * @param filePath Path to the dump.cs file * @returns true if hash was removed */ removeFileFromProcessed(filePath) { return this.hashManager.removeFileHash(filePath); } /** * Clear all processed file hashes */ clearAllProcessedFiles() { this.hashManager.clearAllHashes(); } /** * Get information about processed files * @returns Object with hash storage info */ getProcessedFilesInfo() { return this.hashManager.getInfo(); } /** * Get all processed file hashes * @returns Array of all stored hashes */ getAllProcessedHashes() { return this.hashManager.getAllHashes(); } } exports.IL2CPPIndexer = IL2CPPIndexer; //# sourceMappingURL=indexer.js.map