declare function __non_webpack_require__ ( fn ) ; const fs = __non_webpack_require__ ( 'fs' ) ; import * as JSZip from 'jszip' ; import { Nes } from '../../src/nes/nes' ; import { INoiseChannel , IPulseChannel , PadValue , WaveType } from '../../src/nes/apu' ; import { Util } from '../../src/util/util' ; import { AudioManager } from '../../src/util/audio_manager' ; import { AudioContext } from './audio_context' ; const DEFAULT_MASTER_VOLUME = 0.125 ; const KEY_X = 27 ; const KEY_Z = 29 ; const ARROW_RIGHT = 79 ; const ARROW_LEFT = 80 ; const ARROW_DOWN = 81 ; const ARROW_UP = 82 ; const RETURN = 40 ; const ESCAPE = 41 ; const SPACE = 44 ; const kScanCode2PadValue = { [ ARROW_RIGHT ] : PadValue . R , [ ARROW_LEFT ] : PadValue . L , [ ARROW_DOWN ] : PadValue . D , [ ARROW_UP ] : PadValue . U , [ KEY_X ] : PadValue . A , [ KEY_Z ] : PadValue . B , [ RETURN ] : PadValue . START , [ SPACE ] : PadValue . SELECT , } ; function createMyApp ( ) { const NS = __non_webpack_require__ ( 'node-sdl2' ) ; const SDL = NS . require ( 'SDL' ) ; const SDL_render = NS . require ( 'SDL_render' ) ; const SDL_pixels = NS . require ( 'SDL_pixels' ) ; const App = NS . createAppWithFlags ( SDL . SDL_InitFlags . SDL_INIT_EVERYTHING ) ; const Window = NS . window ; const WIDTH = 256 ; const HEIGHT = 240 ; const TITLE = 'Nesemu' ; const MAX_ELAPSED_TIME = 1000 / 15 ; class MyApp { private win : any ; private texture : any ; private pixels = new Uint8Array ( WIDTH * HEIGHT * 4 ) ; private prevTime = 0 ; private nes : Nes ; private pad = 0 ; private audioManager = new AudioManager ( ) ; constructor ( ) { this . win = new Window ( { title : TITLE , w : WIDTH * 2 , h : HEIGHT * 2 , } ) ; this . win . on ( 'close' , ( ) => { App . quit ( ) ; } ) ; this . win . on ( 'change' , ( ) => { this . render ( ) ; } ) ; this . win . on ( 'keydown' , ( key ) => { if ( key . scancode === ESCAPE ) process . exit ( 0 ) ; const v = kScanCode2PadValue [ key . scancode ] ; if ( v ) this . pad |= v ; } ) ; this . win . on ( 'keyup' , ( key ) => { const v = kScanCode2PadValue [ key . scancode ] ; if ( v ) this . pad &= ~ v ; } ) ; this . texture = this . win . render . createTexture ( 256 , 240 , SDL_pixels . PixelFormat . ABGR8888 , SDL_render . SDL_TextureAccess . SDL_TEXTUREACCESS_STREAMING ) ; this . nes = Nes . create ( ) ; } public run ( romData ) { const result = this . nes . setRomData ( romData ) ; if ( result !== true ) throw result ; this . nes . reset ( ) ; this . setupAudioManager ( ) ; this . nes . setVblankCallback ( ( leftV ) => { this . onVblank ( leftV ) ; } ) ; this . prevTime = Date . now ( ) ; setInterval ( ( ) => { const now = Date . now ( ) ; const elapsedTime = now - this . prevTime ; this . loop ( elapsedTime ) ; this . prevTime = now ; } , 1000 / 100 ) ; } private loop ( elapsedTime ) { this . nes . setPadStatus ( 0 , this . pad ) ; let et = Math . min ( elapsedTime , MAX_ELAPSED_TIME ) ; this . nes . runMilliseconds ( et ) ; } private onVblank ( leftV ) { if ( leftV < 1 ) this . render ( ) ; this . updateAudio ( ) ; } private render ( ) { this . nes . render ( this . pixels ) ; const pitch = WIDTH * 4 ; this . texture . update ( null , this . pixels , pitch ) ; this . win . render . copy ( this . texture , null , null ) ; this . win . render . present ( ) ; } private setupAudioManager ( ) { const waveTypes = this . nes . getChannelWaveTypes ( ) ; for ( const type of waveTypes ) { this . audioManager . addChannel ( type ) ; } } private updateAudio ( ) { const audioManager = this . audioManager ; const nes = this . nes ; const waveTypes = this . nes . getChannelWaveTypes ( ) ; const count = waveTypes . length ; for ( let ch = 0 ; ch < count ; ++ ch ) { const channel = nes . getSoundChannel ( ch ) ; const volume = channel . getVolume ( ) ; audioManager . setChannelVolume ( ch , volume ) ; if ( volume > 0 ) { switch ( waveTypes [ ch ] ) { case WaveType . PULSE : { const pulse = channel as unknown as IPulseChannel ; audioManager . setChannelDutyRatio ( ch , pulse . getDutyRatio ( ) ) ; } case WaveType . TRIANGLE : case WaveType . SAWTOOTH : audioManager . setChannelFrequency ( ch , channel . getFrequency ( ) ) ; break ; case WaveType . NOISE : { const noise = channel as unknown as INoiseChannel ; const [ period , mode ] = noise . getNoisePeriod ( ) ; audioManager . setChannelPeriod ( ch , period , mode ) ; } break ; case WaveType . DMC : break ; } } } } } return new MyApp ( ) ; } function run ( fileName ) { new Promise ( ( resolve , reject ) => { fs . readFile ( fileName , ( err , data ) => err ? reject ( err ) : resolve ( data ) ) ; } ) . then ( ( data ) => { if ( Util . getExt ( fileName ) . toLowerCase ( ) !== 'zip' ) return Promise . resolve ( data ) ; const zip = new JSZip ( ) ; return zip . loadAsync ( data ) . then ( ( loadedZip ) => { for ( let fn of Object . keys ( loadedZip . files ) ) { if ( Util . getExt ( fn ) . toLowerCase ( ) === 'nes' ) return loadedZip . files [ fn ] . async ( 'uint8array' ) ; } return Promise . reject ( 'No .nes file included' ) ; } ) ; } ) . then ( ( romData ) => { const myApp = createMyApp ( ) ; myApp . run ( romData ) ; } ) . catch ( ( error ) => { console . error ( error ) ; process . exit ( 1 ) ; } ) ; } if ( process . argv . length < 3 ) { console . error ( 'ROMFILE' ) ; process . exit ( 1 ) ; } AudioManager . setUp ( AudioContext ) ; AudioManager . setMasterVolume ( DEFAULT_MASTER_VOLUME ) ; run ( process . argv [ 2 ] ) ;