UNPKG

snes-disassembler

Version:

A Super Nintendo (SNES) ROM disassembler for 65816 assembly

616 lines 20.1 kB
/** * SNES Asset Extraction System * * Comprehensive asset extraction for graphics, audio, and text from SNES ROMs * Based on research from snes-mcp-server, zelda3, and snes9x implementations * Enhanced with AI-powered pattern recognition for generic asset detection */ import { AIPatternRecognizer, GraphicsClassification, AudioClassification, TextClassification } from './ai-pattern-recognition'; export interface Tile { data: Uint8Array; width: number; height: number; bitsPerPixel: number; paletteIndex?: number; address: number; metadata?: Record<string, any>; aiClassification?: GraphicsClassification; } export interface Sprite { tiles: Tile[]; width: number; height: number; x: number; y: number; priority: number; paletteIndex: number; hflip: boolean; vflip: boolean; address: number; } export interface Palette { colors: number[]; address: number; format: 'BGR555'; } export interface Background { tilemap: Uint16Array; tileData: Tile[]; width: number; height: number; bitsPerPixel: number; address: number; } export interface BRRSample { data: Uint8Array; loopStart: number; loopEnd: number; sampleRate: number; address: number; name?: string; aiClassification?: AudioClassification; blocks: BRRBlock[]; loopFlag: boolean; endFlag: boolean; pitch: number; adsrEnvelope?: ADSREnvelope; checksumValid: boolean; metadata?: SampleMetadata; } export interface BRRBlock { header: number; data: Uint8Array; shift: number; filter: number; loopFlag: boolean; endFlag: boolean; address: number; valid: boolean; } export interface ADSREnvelope { attack: number; decay: number; sustain: number; release: number; raw: number; } export interface SampleMetadata { instrumentName?: string; originalPitch?: number; fineTune?: number; gain?: number; keyRange?: { low: number; high: number; }; category?: 'instrument' | 'percussion' | 'sfx' | 'voice'; } export interface MusicSequence { data: Uint8Array; tempo: number; channels: ChannelData[]; address: number; name?: string; engine: SPCEngineType; patternTable?: PatternTableEntry[]; trackLength: number; loopPoint?: number; loopLength?: number; timingInfo: TimingInfo; instrumentAssignments: Map<number, number>; effects: SequenceEffect[]; metadata?: SequenceMetadata; } export interface ChannelData { channelNumber: number; notes: NoteEvent[]; velocities: number[]; effects: ChannelEffect[]; instrumentIndex: number; volume: number; pan: number; trackData: Uint8Array; } export interface NoteEvent { note: number; velocity: number; duration: number; timestamp: number; pitch?: number; } export interface ChannelEffect { type: 'pitchBend' | 'vibrato' | 'portamento' | 'tremolo' | 'volume' | 'pan' | 'echo' | 'noise'; parameter1: number; parameter2?: number; timestamp: number; duration?: number; } export interface PatternTableEntry { patternIndex: number; address: number; length: number; loopFlag: boolean; channels: number[]; } export interface TimingInfo { ticksPerBeat: number; beatsPerMeasure: number; tempo: number; timeSignature: { numerator: number; denominator: number; }; totalTicks: number; } export interface SequenceEffect { type: 'globalVolume' | 'globalTempo' | 'echo' | 'noise' | 'masterPitch'; value: number; timestamp: number; } export interface SequenceMetadata { title?: string; composer?: string; game?: string; trackNumber?: number; genre?: string; complexity: 'simple' | 'medium' | 'complex'; estimatedDuration: number; } export type SPCEngineType = 'N-SPC' | 'Akao' | 'Kankichi-kun' | 'HAL' | 'Capcom QSound' | 'Unknown'; export interface SPCEnginePattern { engine: SPCEngineType; confidence: number; characteristics: string[]; driverBaseAddress: number; sampleTableAddress?: number; instrumentTableAddress?: number; commandTableAddress?: number; echoBufferAddress?: number; } export interface SPCDriverVersion { major: number; minor: number; revision?: number; versionString?: string; buildDate?: string; } export interface SPCSoundCommand { opcode: number; name: string; parameters: number[]; description: string; address: number; } export interface SPCInstrument { sampleIndex: number; pitch: number; adsr: ADSREnvelope; gain: number; fineTune: number; keyRange?: { low: number; high: number; }; address: number; name?: string; } export interface SPCSampleMapping { instrumentIndex: number; sampleIndex: number; baseNote: number; sampleRate: number; loopStart: number; loopEnd: number; address: number; } export interface SPCEchoBufferConfig { enabled: boolean; bufferAddress: number; bufferSize: number; delay: number; feedback: number; filterCoefficients: number[]; leftVolume: number; rightVolume: number; channelMask: number; } export interface SampleDirectoryEntry { address: number; pitch: number; adsr: ADSREnvelope; sampleRate?: number; } export interface SPCProgram { code: Uint8Array; samples: BRRSample[]; sequences: MusicSequence[]; address: number; enginePattern?: SPCEnginePattern; driverVersion?: SPCDriverVersion; soundCommandTable?: SPCSoundCommand[]; instrumentTable?: SPCInstrument[]; sampleMappings?: SPCSampleMapping[]; echoBufferConfig?: SPCEchoBufferConfig; } export interface TextString { text: string; encoding: string; address: number; length: number; context?: 'dialogue' | 'menu' | 'item' | 'unknown'; aiClassification?: TextClassification; } export interface DialogueTree { strings: TextString[]; choices: number[]; address: number; } export type GraphicsFormat = '2bpp' | '4bpp' | '8bpp'; export type TextEncoding = 'ascii' | 'shift-jis' | 'custom'; export interface SequenceHeader { address: number; tempo: number; channelMask: number; channelPointers: number[]; engine: SPCEngineType; } export interface ChannelCommandResult { type: 'note' | 'rest' | 'instrument' | 'volume' | 'pan' | 'pitchBend' | 'vibrato' | 'echo' | 'tempo' | 'end'; note?: number; velocity?: number; duration?: number; value?: number; parameter2?: number; pitch?: number; commandLength: number; } /** * Graphics Extraction Module * Extracts tiles, sprites, palettes, and backgrounds from SNES VRAM data */ export declare class GraphicsExtractor { /** * Extract tile data from CHR/VRAM data * Based on SNES planar graphics format research */ extractTiles(data: Uint8Array, format: GraphicsFormat, startAddress?: number, count?: number, aiRecognizer?: AIPatternRecognizer): Promise<Tile[]>; /** * Extract sprite data from OAM and CHR data * Based on zelda3 sprite handling patterns */ extractSprites(oamData: Uint8Array, chrData: Uint8Array, startAddress?: number): Sprite[]; /** * Extract color palettes from CGRAM data * SNES uses 15-bit BGR format: 0BBBBBGGGGGRRRRR */ extractPalettes(cgramData: Uint8Array, startAddress?: number): Palette[]; /** * Extract background tilemaps and associated tile data */ extractBackgrounds(tilemapData: Uint8Array, tileData: Uint8Array, format: GraphicsFormat, startAddress?: number): Promise<Background[]>; private getBytesPerTile; private getBitsPerPixel; /** * Convert SNES planar graphics format to linear pixel data * SNES stores graphics in planar format for hardware efficiency */ private convertPlanarToLinear; } /** * Audio Extraction Module * Extracts SPC700 programs, BRR samples, and music sequences */ export declare class AudioExtractor { /** * Extract complete SPC700 program data from audio RAM including drivers, samples, and sequences * * This method performs comprehensive extraction of SPC700 audio programs by: * - Detecting the SPC engine type (N-SPC, Akao, Kankichi-kun, HAL, etc.) * - Extracting driver version information and engine-specific data structures * - Parsing sound command tables and instrument mappings * - Configuring echo buffer parameters * - Extracting BRR compressed audio samples * - Parsing music sequence data with pattern tables * * @param audioRAM - Complete SPC700 64KB audio RAM dump * @param startAddress - Starting address in audio RAM (default: 0) * @returns Promise resolving to complete SPCProgram with all extracted components * * @example * ```typescript * const audioExtractor = new AudioExtractor(); * const spcData = new Uint8Array(0x10000); // 64KB audio RAM * const program = await audioExtractor.extractSPCData(spcData); * * console.log(`Engine: ${program.enginePattern?.engine}`); * console.log(`Samples: ${program.samples.length}`); * console.log(`Sequences: ${program.sequences.length}`); * ``` * * @see {@link https://snesdev.mesen.ca/wiki/index.php?title=SPC700_Reference} SPC700 Technical Reference * @see {@link SPCProgram} Return type interface * @see {@link SPCEnginePattern} Engine detection results */ extractSPCData(audioRAM: Uint8Array, startAddress?: number): Promise<SPCProgram>; /** * Detect SPC700 engine patterns from audio RAM */ private detectSPCEnginePattern; /** * Extract driver version information based on detected engine */ private extractDriverVersion; /** * Locate and parse sound command tables */ private extractSoundCommandTable; /** * Extract instrument tables and sample mappings */ private extractInstrumentTable; /** * Extract sample mappings */ private extractSampleMappings; /** * Parse echo buffer configuration */ private extractEchoBufferConfig; /** * Extract BRR (Bit Rate Reduction) compressed audio samples with enhanced parsing * * This method implements comprehensive BRR audio sample extraction by: * - Validating BRR block headers for proper format compliance * - Parsing complete sample chains with loop and end flags * - Extracting metadata including ADSR envelopes and pitch data * - Detecting sample rates from engine context or using defaults * - Validating data integrity with checksums * - Classifying samples using AI pattern recognition (optional) * - Finding and parsing sample directory tables when available * * BRR Format Structure: * - Each block: 9 bytes (1 header + 8 data bytes = 16 4-bit samples) * - Header byte: SSSSFFLE (S=shift, FF=filter, L=loop, E=end) * - Four filter types (0-3) with different prediction algorithms * - Loop flag indicates loop start, end flag indicates sample termination * * @param data - Raw audio data containing BRR samples * @param startOffset - Starting offset in data to begin extraction (default: 0) * @param aiRecognizer - Optional AI classifier for sample categorization * @returns Promise resolving to array of extracted BRR samples with metadata * * @example * ```typescript * const audioExtractor = new AudioExtractor(); * const audioData = new Uint8Array(romData.slice(0x20000, 0x40000)); * const samples = await audioExtractor.extractBRRSamples(audioData, 0x200); * * samples.forEach(sample => { * console.log(`Sample at $${sample.address.toString(16)}: ${sample.blocks.length} blocks`); * console.log(`Loop: ${sample.loopFlag}, Category: ${sample.metadata?.category}`); * console.log(`Sample Rate: ${sample.sampleRate}Hz, Pitch: ${sample.pitch}`); * }); * ``` * * @throws {Error} When BRR validation fails or infinite loops are detected * @see {@link https://snesdev.mesen.ca/wiki/index.php?title=BRR} BRR Format Specification * @see {@link BRRSample} Return type interface with all metadata * @see {@link BRRBlock} Individual block structure * @see {@link SampleMetadata} Extracted sample characteristics */ extractBRRSamples(data: Uint8Array, startOffset?: number, aiRecognizer?: AIPatternRecognizer): Promise<BRRSample[]>; /** * Extract a single BRR sample with comprehensive parsing */ private extractSingleBRRSample; /** * Validate BRR block header for proper format with enhanced debugging */ private isValidBRRHeader; /** * Validate individual BRR block data integrity */ private validateBRRBlock; /** * Detect sample rate from SPC700 engine data or return default */ private detectSampleRate; /** * Estimate pitch from BRR sample characteristics */ private estimatePitch; /** * Validate BRR data integrity using checksums */ private validateBRRChecksum; /** * Extract sample metadata from BRR characteristics */ private extractSampleMetadata; /** * Find sample directory table in SPC700 memory */ private findSampleDirectory; /** * Parse ADSR envelope from byte value */ private parseADSR; /** * Enhanced music sequence extraction with comprehensive pattern analysis * * This method implements sophisticated music sequence extraction by: * - Auto-detecting SPC engine type (N-SPC, Akao, HAL, Kankichi-kun, etc.) * - Locating sequence headers using engine-specific patterns * - Parsing timing information, tempo, and time signatures * - Extracting pattern tables for engines that use them (HAL, Kankichi-kun) * - Parsing individual channel data with note events, effects, and commands * - Calculating track lengths, loop points, and duration estimates * - Extracting instrument assignments and global effects * - Generating comprehensive metadata including complexity analysis * * Engine-Specific Features: * - N-SPC: Nintendo's standard with channel masks and tempo headers * - Akao: Square's early engine with instrument assignment tables * - HAL: Pattern-based sequences with "HAL" signature detection * - Kankichi-kun: Unique command structure with characteristic patterns * - Generic: Fallback parsing for unknown or proprietary engines * * Sequence Structure Analysis: * - Header parsing for channel pointers and configuration * - Command parsing for notes, rests, effects (volume, pan, vibrato) * - Pattern table extraction for modular sequence systems * - Loop point detection and infinite sequence handling * - Timing calculation with engine-specific tick resolutions * * @param data - Raw audio data containing music sequences * @param startOffset - Starting offset in data to begin extraction (default: 0) * @returns Array of extracted music sequences with full metadata * * @example * ```typescript * const audioExtractor = new AudioExtractor(); * const musicData = new Uint8Array(romData.slice(0x10000, 0x20000)); * const sequences = audioExtractor.extractSequences(musicData, 0x1000); * * sequences.forEach(seq => { * console.log(`Sequence "${seq.name}" (${seq.engine} engine)`); * console.log(` Tempo: ${seq.tempo} BPM, Duration: ${seq.metadata?.estimatedDuration}s`); * console.log(` Channels: ${seq.channels.length}, Complexity: ${seq.metadata?.complexity}`); * console.log(` Pattern Table: ${seq.patternTable ? 'Yes' : 'No'}`); * * seq.channels.forEach(ch => { * console.log(` Channel ${ch.channelNumber}: ${ch.notes.length} notes, ${ch.effects.length} effects`); * }); * }); * ``` * * @see {@link https://snesdev.mesen.ca/wiki/index.php?title=SPC700_Reference} SPC700 Audio System * @see {@link MusicSequence} Return type interface with full metadata * @see {@link ChannelData} Individual channel structure * @see {@link TimingInfo} Tempo and timing information */ extractSequences(data: Uint8Array, startOffset?: number): MusicSequence[]; /** * Find sequence headers based on engine-specific patterns */ private findSequenceHeaders; /** * Find N-SPC sequence headers (Nintendo's sound engine) */ private findNSPCSequenceHeaders; /** * Find Akao sequence headers (Square's early sound engine) */ private findAkaoSequenceHeaders; /** * Find HAL sequence headers (HAL Laboratory's sound engine) */ private findHALSequenceHeaders; /** * Find Kankichi-kun sequence headers */ private findKankichiSequenceHeaders; /** * Generic sequence header detection for unknown engines */ private findGenericSequenceHeaders; /** * Parse complete sequence data from header */ private parseSequenceData; /** * Parse timing information from sequence header */ private parseTimingInfo; /** * Parse pattern table for engines that use them */ private parsePatternTable; /** * Parse channel data from sequence */ private parseChannelData; /** * Parse individual channel track data */ private parseChannelTrack; /** * Helper methods for sequence extraction */ private extractSequencesByPattern; private isKankichiCommand; private looksLikeSequenceHeader; private getPatternChannels; private getActiveChannels; private getChannelStartAddress; private isEndOfTrack; private parseChannelCommand; private parseNSPCCommand; private parseAkaoCommand; private parseHALCommand; private parseKankichiCommand; private parseGenericCommand; private calculateTrackLength; private extractInstrumentAssignments; private parseSequenceEffects; private generateSequenceMetadata; private findSequenceEnd; } /** * Text Extraction Module * Extracts text strings with encoding detection */ export declare class TextExtractor { private customTables; /** * Detect text encoding used in the ROM */ detectEncoding(data: Uint8Array): TextEncoding; /** * Extract text strings from ROM data */ extractStrings(data: Uint8Array, encoding: TextEncoding, startAddress?: number, minLength?: number, aiRecognizer?: AIPatternRecognizer): Promise<TextString[]>; private extractASCIIStrings; private extractShiftJISStrings; private extractCustomStrings; private guessTextContext; /** * Set custom character table for proprietary encodings */ setCustomTable(name: string, table: Map<number, string>): void; } /** * Main Asset Extractor Class * Coordinates all asset extraction operations */ export declare class AssetExtractor { private graphicsExtractor; private audioExtractor; private textExtractor; private aiPatternRecognizer?; constructor(enableAI?: boolean, modelPath?: string); /** * Extract all assets from ROM data with analysis guidance */ extractAssets(romData: Uint8Array, analysisResults?: any): Promise<{ graphics: { tiles: Tile[]; sprites: Sprite[]; palettes: Palette[]; backgrounds: Background[]; }; audio: { spcProgram?: SPCProgram; samples: BRRSample[]; sequences: MusicSequence[]; }; text: { strings: TextString[]; dialogues: DialogueTree[]; }; }>; getGraphicsExtractor(): GraphicsExtractor; getAudioExtractor(): AudioExtractor; getTextExtractor(): TextExtractor; } //# sourceMappingURL=asset-extractor.d.ts.map