snes-disassembler
Version:
A Super Nintendo (SNES) ROM disassembler for 65816 assembly
616 lines • 20.1 kB
TypeScript
/**
* 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