UNPKG

snes-disassembler

Version:

A Super Nintendo (SNES) ROM disassembler for 65816 assembly

554 lines 20.9 kB
"use strict"; /** * CLI Session Manager * * Handles session state persistence, recent files, and user preferences */ 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.sessionManager = exports.SessionManager = void 0; const fs = __importStar(require("fs/promises")); const path = __importStar(require("path")); const os = __importStar(require("os")); class SessionManager { constructor() { const configDir = path.join(os.homedir(), '.snes-disassembler'); this.sessionFile = path.join(configDir, 'session.json'); this.session = this.getDefaultSession(); } getDefaultSession() { return { recentFiles: [], preferences: { defaultOutputDir: './output', globalOutputDir: undefined, // No global output directory set by default defaultFormat: 'ca65', showHelp: true, confirmActions: true, maxRecentFiles: 10 }, projects: [] }; } async load() { try { // Ensure config directory exists await fs.mkdir(path.dirname(this.sessionFile), { recursive: true }); // Try to load existing session const data = await fs.readFile(this.sessionFile, 'utf-8'); this.session = { ...this.getDefaultSession(), ...JSON.parse(data) }; } catch (error) { // File doesn't exist or is corrupted, use defaults this.session = this.getDefaultSession(); } return this.session; } async save() { try { await fs.mkdir(path.dirname(this.sessionFile), { recursive: true }); await fs.writeFile(this.sessionFile, JSON.stringify(this.session, null, 2)); } catch (error) { console.warn('Failed to save session:', error); } } async addRecentFile(filePath, romInfo) { const stats = await fs.stat(filePath); const recentFile = { path: filePath, name: path.basename(filePath), size: this.formatFileSize(stats.size), lastUsed: new Date().toISOString(), romInfo }; // Remove existing entry if present this.session.recentFiles = this.session.recentFiles.filter(f => f.path !== filePath); // Add to beginning this.session.recentFiles.unshift(recentFile); // Limit to max recent files const maxFiles = this.session.preferences.maxRecentFiles ?? 10; this.session.recentFiles = this.session.recentFiles.slice(0, maxFiles); await this.save(); } getRecentFiles() { return this.session.recentFiles; } async addProject(project) { // Remove existing project with same name this.session.projects = this.session.projects.filter(p => p.name !== project.name); // Add new project this.session.projects.unshift(project); // Limit to reasonable number of projects this.session.projects = this.session.projects.slice(0, 20); await this.save(); } getProjects() { return this.session.projects; } updatePreferences(preferences) { this.session.preferences = { ...this.session.preferences, ...preferences }; } getPreferences() { return this.session.preferences; } setCurrentROM(romFile) { this.session.currentROM = romFile; } getCurrentROM() { return this.session.currentROM; } formatFileSize(bytes) { const units = ['B', 'KB', 'MB', 'GB']; let size = bytes; let unitIndex = 0; while (size >= 1024 && unitIndex < units.length - 1) { size /= 1024; unitIndex++; } return `${size.toFixed(1)} ${units[unitIndex]}`; } async clearRecentFiles() { this.session.recentFiles = []; await this.save(); } async removeRecentFile(filePath) { this.session.recentFiles = this.session.recentFiles.filter(f => f.path !== filePath); await this.save(); } // Clean up recent files that no longer exist async cleanupRecentFiles() { const validFiles = []; for (const file of this.session.recentFiles) { try { await fs.access(file.path); validFiles.push(file); } catch { // File no longer exists, skip it } } if (validFiles.length !== this.session.recentFiles.length) { this.session.recentFiles = validFiles; await this.save(); } } // Global output directory management /** * Set the global default output directory for the current session * @param outputDir - The directory path to set as global default */ async setGlobalOutputDir(outputDir) { // Validate the directory path try { // Try to create the directory if it doesn't exist await fs.mkdir(outputDir, { recursive: true }); // Verify we can access it await fs.access(outputDir); } catch (error) { throw new Error(`Cannot access or create output directory: ${outputDir}`); } this.session.preferences.globalOutputDir = path.resolve(outputDir); await this.save(); } /** * Get the global default output directory for the current session * @returns The global output directory path, or undefined if not set */ getGlobalOutputDir() { return this.session.preferences.globalOutputDir; } /** * Clear the global default output directory setting */ async clearGlobalOutputDir() { this.session.preferences.globalOutputDir = undefined; await this.save(); } /** * Get the effective output directory - global if set, otherwise default * @param specificDir - Optional specific directory that takes precedence * @returns The output directory to use */ getEffectiveOutputDir(specificDir) { if (specificDir) { return specificDir; } return this.session.preferences.globalOutputDir ?? this.session.preferences.defaultOutputDir ?? './output'; } /** * Check if a global output directory is currently set * @returns True if global output directory is set, false otherwise */ hasGlobalOutputDir() { return !!this.session.preferences.globalOutputDir; } // Advanced Preferences Management /** * Update advanced analysis options preferences * @param options - Advanced options to update */ async updateAdvancedOptions(options) { if (!this.session.preferences.advancedOptions) { this.session.preferences.advancedOptions = {}; } this.session.preferences.advancedOptions = { ...this.session.preferences.advancedOptions, ...options }; await this.save(); } /** * Get advanced analysis options preferences * @returns Current advanced options preferences */ getAdvancedOptions() { return this.session.preferences.advancedOptions ?? {}; } /** * Update asset extraction preferences * @param preferences - Asset preferences to update */ async updateAssetPreferences(preferences) { if (!this.session.preferences.assetPreferences) { this.session.preferences.assetPreferences = {}; } this.session.preferences.assetPreferences = { ...this.session.preferences.assetPreferences, ...preferences }; await this.save(); } /** * Get asset extraction preferences * @returns Current asset preferences */ getAssetPreferences() { return this.session.preferences.assetPreferences ?? {}; } /** * Update BRR audio preferences * @param preferences - BRR preferences to update */ async updateBRRPreferences(preferences) { if (!this.session.preferences.brrPreferences) { this.session.preferences.brrPreferences = {}; } this.session.preferences.brrPreferences = { ...this.session.preferences.brrPreferences, ...preferences }; await this.save(); } /** * Get BRR audio preferences * @returns Current BRR preferences */ getBRRPreferences() { return this.session.preferences.brrPreferences ?? {}; } /** * Update analysis preferences * @param preferences - Analysis preferences to update */ async updateAnalysisPreferences(preferences) { if (!this.session.preferences.analysisPreferences) { this.session.preferences.analysisPreferences = {}; } this.session.preferences.analysisPreferences = { ...this.session.preferences.analysisPreferences, ...preferences }; await this.save(); } /** * Get analysis preferences * @returns Current analysis preferences */ getAnalysisPreferences() { return this.session.preferences.analysisPreferences ?? {}; } /** * Update output format preferences * @param preferences - Format preferences to update */ async updateFormatPreferences(preferences) { if (!this.session.preferences.formatPreferences) { this.session.preferences.formatPreferences = {}; } this.session.preferences.formatPreferences = { ...this.session.preferences.formatPreferences, ...preferences }; await this.save(); } /** * Get output format preferences * @returns Current format preferences */ getFormatPreferences() { return this.session.preferences.formatPreferences ?? {}; } /** * Update UI/UX preferences * @param preferences - UI preferences to update */ async updateUIPreferences(preferences) { if (!this.session.preferences.uiPreferences) { this.session.preferences.uiPreferences = {}; } this.session.preferences.uiPreferences = { ...this.session.preferences.uiPreferences, ...preferences }; await this.save(); } /** * Get UI/UX preferences * @returns Current UI preferences */ getUIPreferences() { return this.session.preferences.uiPreferences ?? {}; } /** * Update AI feature preferences * @param preferences - AI preferences to update */ async updateAIPreferences(preferences) { if (!this.session.preferences.aiPreferences) { this.session.preferences.aiPreferences = {}; } this.session.preferences.aiPreferences = { ...this.session.preferences.aiPreferences, ...preferences }; await this.save(); } /** * Get AI feature preferences * @returns Current AI preferences */ getAIPreferences() { return this.session.preferences.aiPreferences ?? {}; } /** * Apply saved preferences to CLI options * @param baseOptions - Base CLI options to extend * @returns CLI options with preferences applied */ applyPreferencesToOptions(baseOptions = {}) { const prefs = this.session.preferences; const advanced = prefs.advancedOptions ?? {}; const asset = prefs.assetPreferences ?? {}; const brr = prefs.brrPreferences ?? {}; // const analysis = prefs.analysisPreferences ?? {}; // unused // const format = prefs.formatPreferences ?? {}; // unused const ui = prefs.uiPreferences ?? {}; // const ai = prefs.aiPreferences ?? {}; // unused return { ...baseOptions, // Apply basic preferences format: baseOptions.format ?? prefs.defaultFormat ?? 'ca65', outputDir: baseOptions.outputDir ?? this.getEffectiveOutputDir(), verbose: baseOptions.verbose ?? (ui.showProgressBars !== false), // Apply advanced analysis options analysis: baseOptions.analysis ?? advanced.analysis, enhancedDisasm: baseOptions.enhancedDisasm ?? advanced.enhancedDisasm, bankAware: baseOptions.bankAware ?? advanced.bankAware, detectFunctions: baseOptions.detectFunctions ?? advanced.detectFunctions, generateDocs: baseOptions.generateDocs ?? advanced.generateDocs, extractAssets: baseOptions.extractAssets ?? advanced.extractAssets, quality: baseOptions.quality ?? advanced.quality, disableAI: baseOptions.disableAI ?? advanced.disableAI, // Apply asset extraction preferences assetTypes: baseOptions.assetTypes ?? asset.defaultAssetTypes?.join(','), assetFormats: baseOptions.assetFormats ?? asset.defaultAssetFormats?.join(','), // Apply BRR audio preferences brrSampleRate: baseOptions.brrSampleRate ?? brr.defaultSampleRate?.toString(), brrEnableLooping: baseOptions.brrEnableLooping ?? brr.enableLooping, brrMaxSamples: baseOptions.brrMaxSamples ?? brr.maxSamples?.toString() }; } /** * Get preferences summary for display * @returns Human-readable summary of current preferences */ getPreferencesSummary() { const prefs = this.session.preferences; const lines = []; lines.push('Current User Preferences:'); lines.push('=' + '='.repeat(50)); // Basic preferences lines.push(''); lines.push('Basic Settings:'); lines.push(` Default Format: ${prefs.defaultFormat ?? 'ca65'}`); lines.push(` Default Output Dir: ${prefs.defaultOutputDir ?? './output'}`); lines.push(` Global Output Dir: ${prefs.globalOutputDir ?? 'Not set'}`); lines.push(` Max Recent Files: ${prefs.maxRecentFiles ?? 10}`); lines.push(` Show Help: ${prefs.showHelp !== false ? 'Yes' : 'No'}`); lines.push(` Confirm Actions: ${prefs.confirmActions !== false ? 'Yes' : 'No'}`); // Advanced options if (prefs.advancedOptions) { lines.push(''); lines.push('Advanced Analysis Options:'); const advanced = prefs.advancedOptions; Object.entries(advanced).forEach(([key, value]) => { if (value !== undefined) { lines.push(` ${key}: ${value ? 'Enabled' : 'Disabled'}`); } }); } // Asset preferences if (prefs.assetPreferences) { lines.push(''); lines.push('Asset Extraction Preferences:'); const asset = prefs.assetPreferences; if (asset.defaultAssetTypes) { lines.push(` Default Asset Types: ${asset.defaultAssetTypes.join(', ')}`); } if (asset.defaultAssetFormats) { lines.push(` Default Asset Formats: ${asset.defaultAssetFormats.join(', ')}`); } if (asset.defaultAssetOutputDir) { lines.push(` Default Asset Output Dir: ${asset.defaultAssetOutputDir}`); } } // BRR preferences if (prefs.brrPreferences) { lines.push(''); lines.push('BRR Audio Preferences:'); const brr = prefs.brrPreferences; if (brr.defaultSampleRate) { lines.push(` Default Sample Rate: ${brr.defaultSampleRate} Hz`); } if (brr.enableLooping !== undefined) { lines.push(` Enable Looping: ${brr.enableLooping ? 'Yes' : 'No'}`); } if (brr.maxSamples) { lines.push(` Max Samples: ${brr.maxSamples}`); } if (brr.defaultOutputFormat) { lines.push(` Default Output Format: ${brr.defaultOutputFormat}`); } } // Analysis preferences if (prefs.analysisPreferences) { lines.push(''); lines.push('Analysis Preferences:'); const analysis = prefs.analysisPreferences; if (analysis.defaultAnalysisTypes) { lines.push(` Default Analysis Types: ${analysis.defaultAnalysisTypes.join(', ')}`); } if (analysis.defaultAnalysisOutputDir) { lines.push(` Default Analysis Output Dir: ${analysis.defaultAnalysisOutputDir}`); } if (analysis.generateHtmlReports !== undefined) { lines.push(` Generate HTML Reports: ${analysis.generateHtmlReports ? 'Yes' : 'No'}`); } if (analysis.includeQualityMetrics !== undefined) { lines.push(` Include Quality Metrics: ${analysis.includeQualityMetrics ? 'Yes' : 'No'}`); } } // AI preferences if (prefs.aiPreferences) { lines.push(''); lines.push('AI Feature Preferences:'); const ai = prefs.aiPreferences; if (ai.enableAIFeatures !== undefined) { lines.push(` Enable AI Features: ${ai.enableAIFeatures ? 'Yes' : 'No'}`); } if (ai.aiConfidenceThreshold) { lines.push(` AI Confidence Threshold: ${ai.aiConfidenceThreshold}`); } if (ai.useContextualNaming !== undefined) { lines.push(` Use Contextual Naming: ${ai.useContextualNaming ? 'Yes' : 'No'}`); } if (ai.generateAIDocumentation !== undefined) { lines.push(` Generate AI Documentation: ${ai.generateAIDocumentation ? 'Yes' : 'No'}`); } } return lines.join('\n'); } /** * Reset all preferences to defaults */ async resetPreferences() { this.session.preferences = this.getDefaultSession().preferences; await this.save(); } /** * Export preferences to a JSON file * @param filePath - Path to export preferences to */ async exportPreferences(filePath) { const prefsData = { exportedAt: new Date().toISOString(), version: '1.0.0', preferences: this.session.preferences }; await fs.writeFile(filePath, JSON.stringify(prefsData, null, 2)); } /** * Import preferences from a JSON file * @param filePath - Path to import preferences from */ async importPreferences(filePath) { try { const data = await fs.readFile(filePath, 'utf-8'); const prefsData = JSON.parse(data); if (prefsData.preferences) { // Merge with current preferences, preserving structure this.session.preferences = { ...this.session.preferences, ...prefsData.preferences }; await this.save(); } else { throw new Error('Invalid preferences file format'); } } catch (error) { throw new Error(`Failed to import preferences: ${error instanceof Error ? error.message : error}`); } } } exports.SessionManager = SessionManager; // Singleton instance exports.sessionManager = new SessionManager(); //# sourceMappingURL=session-manager.js.map