game.js 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. const SCREEN_WIDTH = 256;
  2. const SCREEN_HEIGHT = 240;
  3. const SAMPLE_REDUCTION = 4;
  4. const EventEmitter = require('events')
  5. const fs = require('fs')
  6. const jsnes = require('jsnes')
  7. const move = require('move-terminal-cursor')
  8. const chunk = (a, l) => Array(Math.ceil(a.length / l)).fill().map((_, i) => (a.slice ? a.slice(i * l, i * l + l) : a.substr(i * l, i * l + 1)))
  9. const sample = (a, n) => Array(Math.floor(a.length / n)).fill().map((_, i) => a[i * n])
  10. class Game extends EventEmitter {
  11. constructor() {
  12. super()
  13. const rom = fs.readFileSync('./Super Mario Bros. (World).nes', 'binary')
  14. this.nes = new jsnes.NES({
  15. onFrame: this.onFrame.bind(this)
  16. })
  17. this.nes.loadROM(rom)
  18. this.frameCount = 0
  19. this.packetBuffer = new ArrayBuffer(256 * 4 + SCREEN_WIDTH * SCREEN_HEIGHT)
  20. this.paletteBuffer = new Uint32Array(this.packetBuffer)
  21. this.pixelBuffer = new Uint8ClampedArray(this.packetBuffer, 256 * 4)
  22. this.frameBuffer = new ArrayBuffer(SCREEN_WIDTH * SCREEN_HEIGHT * 4)
  23. this.screen32 = new Uint32Array(this.frameBuffer)
  24. this.nes.ppu.buffer = this.screen32
  25. this.frameBytes = new Uint8ClampedArray(this.frameBuffer)
  26. }
  27. onFrame(frameBuffer) {
  28. // if (this.frameCount++ % SAMPLE_REDUCTION) {
  29. const colorMap = new Map()
  30. for (let i = 0; i < 256; i++) {
  31. this.paletteBuffer[i] = this.nes.ppu.palTable.curTable[i]
  32. colorMap.set(this.nes.ppu.palTable.curTable[i], i)
  33. }
  34. for (let i = 0; i < this.screen32.length; i++) {
  35. this.pixelBuffer[i] = colorMap.get(this.screen32[i])
  36. }
  37. this.emit('frame', this.packetBuffer)
  38. // }
  39. // if (true) {//(frameCount++ % 32 === 0) {
  40. // //move('toPos', {row: 1, col: 1})
  41. // const lines = sample(chunk(frameBuffer, SCREEN_WIDTH), SAMPLE_REDUCTION)
  42. // .map(line => sample(line, SAMPLE_REDUCTION))
  43. // //console.log(lines.map(l => Buffer.from(l).toString('hex')).join('\n'))
  44. // // console.log('\n\n\n' + chunk(Buffer.from(frameBuffer).toString('hex'), SCREEN_WIDTH * 2).join('\n'))
  45. // }
  46. }
  47. start() {
  48. if (!this.timer) this.timer = setInterval(() => this.nes.frame(), 1000 / 60)
  49. }
  50. stop() {
  51. if (this.timer) {
  52. clearInterval(this.timer)
  53. this.timer = null
  54. }
  55. }
  56. }
  57. module.exports = Game