pijou commited on
Commit
d5a0194
·
verified ·
1 Parent(s): 026c297

Add 1 files

Browse files
Files changed (1) hide show
  1. index.html +243 -70
index.html CHANGED
@@ -128,6 +128,51 @@
128
  background-size: 100% 2px, 3px 100%;
129
  pointer-events: none;
130
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  </style>
132
  </head>
133
  <body class="dark-mode">
@@ -166,7 +211,10 @@
166
  <!-- Emulator Container -->
167
  <div class="md:col-span-2">
168
  <div class="bg-black p-4 rounded-lg crt-effect scanlines">
169
- <div id="nesEmulator" class="mx-auto" style="width: 100%; height: 400px;"></div>
 
 
 
170
  </div>
171
 
172
  <div class="flex flex-wrap justify-center mt-4 gap-2">
@@ -185,6 +233,9 @@
185
  <button id="nesReset" class="btn-pixel">
186
  <i class="fas fa-redo mr-2"></i> Reset
187
  </button>
 
 
 
188
  </div>
189
  </div>
190
 
@@ -202,6 +253,12 @@
202
  <li>Shift: Select</li>
203
  </ul>
204
  <p class="mt-4 text-xs text-gray-400">Upload your own NES ROMs to play</p>
 
 
 
 
 
 
205
  </div>
206
  </div>
207
  </div>
@@ -213,7 +270,10 @@
213
  <!-- Emulator Container -->
214
  <div class="md:col-span-2">
215
  <div class="bg-black p-4 rounded-lg crt-effect scanlines">
216
- <div id="gbaEmulator" class="mx-auto" style="width: 100%; height: 400px;"></div>
 
 
 
217
  </div>
218
 
219
  <div class="flex flex-wrap justify-center mt-4 gap-2">
@@ -388,25 +448,21 @@
388
  controlsSection.classList.remove('hidden');
389
  });
390
 
391
- // NES Emulator
392
  const nes = new jsnes.NES({
393
  onFrame: function(frameBuffer) {
394
- const canvas = document.createElement('canvas');
395
- canvas.width = 256;
396
- canvas.height = 240;
397
  const context = canvas.getContext('2d');
398
  const imageData = context.createImageData(canvas.width, canvas.height);
399
 
400
  for (let i = 0; i < frameBuffer.length; i++) {
401
- imageData.data[i * 4] = frameBuffer[i] >> 16 & 0xFF;
402
- imageData.data[i * 4 + 1] = frameBuffer[i] >> 8 & 0xFF;
403
- imageData.data[i * 4 + 2] = frameBuffer[i] & 0xFF;
404
- imageData.data[i * 4 + 3] = 0xFF;
405
  }
406
 
407
  context.putImageData(imageData, 0, 0);
408
- document.getElementById('nesEmulator').innerHTML = '';
409
- document.getElementById('nesEmulator').appendChild(canvas);
410
  },
411
  onAudioSample: function(left, right) {
412
  // Audio handling would go here
@@ -414,32 +470,33 @@
414
  });
415
 
416
  // NES Controls
 
 
 
 
 
 
 
 
 
 
 
417
  document.addEventListener('keydown', (e) => {
418
  if (!nesSection.classList.contains('hidden')) {
419
- switch(e.keyCode) {
420
- case 38: nes.buttonDown(1, jsnes.Controller.BUTTON_UP); break; // Up
421
- case 40: nes.buttonDown(1, jsnes.Controller.BUTTON_DOWN); break; // Down
422
- case 37: nes.buttonDown(1, jsnes.Controller.BUTTON_LEFT); break; // Left
423
- case 39: nes.buttonDown(1, jsnes.Controller.BUTTON_RIGHT); break; // Right
424
- case 90: nes.buttonDown(1, jsnes.Controller.BUTTON_A); break; // Z (A)
425
- case 88: nes.buttonDown(1, jsnes.Controller.BUTTON_B); break; // X (B)
426
- case 13: nes.buttonDown(1, jsnes.Controller.BUTTON_START); break; // Enter (Start)
427
- case 16: nes.buttonDown(1, jsnes.Controller.BUTTON_SELECT); break; // Shift (Select)
428
  }
429
  }
430
  });
431
 
432
  document.addEventListener('keyup', (e) => {
433
  if (!nesSection.classList.contains('hidden')) {
434
- switch(e.keyCode) {
435
- case 38: nes.buttonUp(1, jsnes.Controller.BUTTON_UP); break;
436
- case 40: nes.buttonUp(1, jsnes.Controller.BUTTON_DOWN); break;
437
- case 37: nes.buttonUp(1, jsnes.Controller.BUTTON_LEFT); break;
438
- case 39: nes.buttonUp(1, jsnes.Controller.BUTTON_RIGHT); break;
439
- case 90: nes.buttonUp(1, jsnes.Controller.BUTTON_A); break;
440
- case 88: nes.buttonUp(1, jsnes.Controller.BUTTON_B); break;
441
- case 13: nes.buttonUp(1, jsnes.Controller.BUTTON_START); break;
442
- case 16: nes.buttonUp(1, jsnes.Controller.BUTTON_SELECT); break;
443
  }
444
  }
445
  });
@@ -447,6 +504,7 @@
447
  // NES File Handling
448
  const nesFileInput = document.getElementById('nesFileInput');
449
  const nesLoadBtn = document.getElementById('nesLoad');
 
450
 
451
  nesLoadBtn.addEventListener('click', () => {
452
  nesFileInput.click();
@@ -456,43 +514,136 @@
456
  const file = e.target.files[0];
457
  if (!file) return;
458
 
 
 
 
 
459
  const reader = new FileReader();
460
  reader.onload = (e) => {
461
- const rom = new Uint8Array(e.target.result);
462
- nes.loadROM(rom);
463
-
464
- // Update game info
465
- document.getElementById('nesGameInfo').innerHTML = `
466
- <p class="mb-2"><span class="text-purple-400">${file.name}</span></p>
467
- <p class="mb-2">Controls:</p>
468
- <ul class="list-disc pl-5">
469
- <li>Arrow Keys: D-Pad</li>
470
- <li>Z: A Button</li>
471
- <li>X: B Button</li>
472
- <li>Enter: Start</li>
473
- <li>Shift: Select</li>
474
- </ul>
475
- <p class="mt-4 text-xs text-gray-400">Press Save State to save your progress</p>
476
- `;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
477
  };
 
 
 
 
 
 
478
  reader.readAsArrayBuffer(file);
479
  });
480
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
481
  // NES Save/Load State
482
  let nesState = null;
 
 
483
 
484
  document.getElementById('nesSaveState').addEventListener('click', () => {
485
- nesState = nes.toJSON();
486
- alert('Game state saved!');
 
 
 
 
 
 
487
  });
488
 
489
  document.getElementById('nesLoadState').addEventListener('click', () => {
490
  if (nesState) {
491
- nes.fromJSON(nesState);
492
- alert('Game state loaded!');
 
 
 
 
 
 
493
  } else {
494
- alert('No saved state found!');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
495
  }
 
496
  });
497
 
498
  // NES Fullscreen
@@ -509,7 +660,19 @@
509
 
510
  // NES Reset
511
  document.getElementById('nesReset').addEventListener('click', () => {
 
 
 
 
 
512
  nes.reset();
 
 
 
 
 
 
 
513
  });
514
 
515
  // GBA Emulator
@@ -694,27 +857,37 @@
694
  const gamepad = gamepads[i];
695
  if (!gamepad) continue;
696
 
697
- // Handle gamepad input
698
- // This would need to be implemented based on the emulator's input handling
699
- // The following is a placeholder implementation
700
-
701
- // Example: Check if A button (usually index 0) is pressed
702
- if (gamepad.buttons[0] && gamepad.buttons[0].pressed) {
703
- // Handle A button press
704
- }
705
-
706
- // Example: Check D-pad or analog stick
707
- if (gamepad.axes[1] < -0.5) {
708
- // Up pressed
709
- } else if (gamepad.axes[1] > 0.5) {
710
- // Down pressed
 
 
 
 
 
 
 
 
 
 
 
 
 
711
  }
712
 
713
- if (gamepad.axes[0] < -0.5) {
714
- // Left pressed
715
- } else if (gamepad.axes[0] > 0.5) {
716
- // Right pressed
717
- }
718
  }
719
 
720
  requestAnimationFrame(pollGamepads);
 
128
  background-size: 100% 2px, 3px 100%;
129
  pointer-events: none;
130
  }
131
+
132
+ /* Emulator canvas styling */
133
+ #nesCanvas {
134
+ image-rendering: -moz-crisp-edges;
135
+ image-rendering: -webkit-crisp-edges;
136
+ image-rendering: pixelated;
137
+ image-rendering: crisp-edges;
138
+ width: 100%;
139
+ height: 100%;
140
+ }
141
+
142
+ /* Loading spinner */
143
+ .loader {
144
+ border: 5px solid #f3f3f3;
145
+ border-top: 5px solid #ff00de;
146
+ border-radius: 50%;
147
+ width: 50px;
148
+ height: 50px;
149
+ animation: spin 1s linear infinite;
150
+ margin: 20px auto;
151
+ }
152
+
153
+ @keyframes spin {
154
+ 0% { transform: rotate(0deg); }
155
+ 100% { transform: rotate(360deg); }
156
+ }
157
+
158
+ /* Status messages */
159
+ .status-message {
160
+ background-color: rgba(0, 0, 0, 0.7);
161
+ color: white;
162
+ padding: 10px;
163
+ border-radius: 5px;
164
+ text-align: center;
165
+ margin-top: 10px;
166
+ font-size: 0.8rem;
167
+ }
168
+
169
+ .status-success {
170
+ color: #00ff00;
171
+ }
172
+
173
+ .status-error {
174
+ color: #ff0000;
175
+ }
176
  </style>
177
  </head>
178
  <body class="dark-mode">
 
211
  <!-- Emulator Container -->
212
  <div class="md:col-span-2">
213
  <div class="bg-black p-4 rounded-lg crt-effect scanlines">
214
+ <div id="nesEmulator" class="mx-auto" style="width: 100%; height: 400px;">
215
+ <canvas id="nesCanvas" width="256" height="240"></canvas>
216
+ </div>
217
+ <div id="nesStatus" class="status-message">Ready to load NES ROM</div>
218
  </div>
219
 
220
  <div class="flex flex-wrap justify-center mt-4 gap-2">
 
233
  <button id="nesReset" class="btn-pixel">
234
  <i class="fas fa-redo mr-2"></i> Reset
235
  </button>
236
+ <button id="nesPause" class="btn-pixel">
237
+ <i class="fas fa-pause mr-2"></i> Pause
238
+ </button>
239
  </div>
240
  </div>
241
 
 
253
  <li>Shift: Select</li>
254
  </ul>
255
  <p class="mt-4 text-xs text-gray-400">Upload your own NES ROMs to play</p>
256
+ <div class="mt-4">
257
+ <h4 class="text-lg font-bold mb-2 text-purple-400">Sample ROMs</h4>
258
+ <button id="loadSample1" class="btn-pixel text-xs px-3 py-1 mb-2">Super Mario Bros</button>
259
+ <button id="loadSample2" class="btn-pixel text-xs px-3 py-1 mb-2">Donkey Kong</button>
260
+ <button id="loadSample3" class="btn-pixel text-xs px-3 py-1">The Legend of Zelda</button>
261
+ </div>
262
  </div>
263
  </div>
264
  </div>
 
270
  <!-- Emulator Container -->
271
  <div class="md:col-span-2">
272
  <div class="bg-black p-4 rounded-lg crt-effect scanlines">
273
+ <div id="gbaEmulator" class="mx-auto" style="width: 100%; height: 400px;">
274
+ <div class="loader"></div>
275
+ <p class="text-center">GBA emulator loading...</p>
276
+ </div>
277
  </div>
278
 
279
  <div class="flex flex-wrap justify-center mt-4 gap-2">
 
448
  controlsSection.classList.remove('hidden');
449
  });
450
 
451
+ // NES Emulator Setup
452
  const nes = new jsnes.NES({
453
  onFrame: function(frameBuffer) {
454
+ const canvas = document.getElementById('nesCanvas');
 
 
455
  const context = canvas.getContext('2d');
456
  const imageData = context.createImageData(canvas.width, canvas.height);
457
 
458
  for (let i = 0; i < frameBuffer.length; i++) {
459
+ imageData.data[i * 4] = frameBuffer[i] >> 16 & 0xFF; // R
460
+ imageData.data[i * 4 + 1] = frameBuffer[i] >> 8 & 0xFF; // G
461
+ imageData.data[i * 4 + 2] = frameBuffer[i] & 0xFF; // B
462
+ imageData.data[i * 4 + 3] = 0xFF; // A
463
  }
464
 
465
  context.putImageData(imageData, 0, 0);
 
 
466
  },
467
  onAudioSample: function(left, right) {
468
  // Audio handling would go here
 
470
  });
471
 
472
  // NES Controls
473
+ const keyMap = {
474
+ 38: jsnes.Controller.BUTTON_UP, // Up arrow
475
+ 40: jsnes.Controller.BUTTON_DOWN, // Down arrow
476
+ 37: jsnes.Controller.BUTTON_LEFT, // Left arrow
477
+ 39: jsnes.Controller.BUTTON_RIGHT, // Right arrow
478
+ 90: jsnes.Controller.BUTTON_A, // Z (A button)
479
+ 88: jsnes.Controller.BUTTON_B, // X (B button)
480
+ 13: jsnes.Controller.BUTTON_START, // Enter (Start)
481
+ 16: jsnes.Controller.BUTTON_SELECT // Shift (Select)
482
+ };
483
+
484
  document.addEventListener('keydown', (e) => {
485
  if (!nesSection.classList.contains('hidden')) {
486
+ const button = keyMap[e.keyCode];
487
+ if (button !== undefined) {
488
+ nes.buttonDown(1, button);
489
+ e.preventDefault();
 
 
 
 
 
490
  }
491
  }
492
  });
493
 
494
  document.addEventListener('keyup', (e) => {
495
  if (!nesSection.classList.contains('hidden')) {
496
+ const button = keyMap[e.keyCode];
497
+ if (button !== undefined) {
498
+ nes.buttonUp(1, button);
499
+ e.preventDefault();
 
 
 
 
 
500
  }
501
  }
502
  });
 
504
  // NES File Handling
505
  const nesFileInput = document.getElementById('nesFileInput');
506
  const nesLoadBtn = document.getElementById('nesLoad');
507
+ const nesStatus = document.getElementById('nesStatus');
508
 
509
  nesLoadBtn.addEventListener('click', () => {
510
  nesFileInput.click();
 
514
  const file = e.target.files[0];
515
  if (!file) return;
516
 
517
+ // Show loading status
518
+ nesStatus.textContent = `Loading ${file.name}...`;
519
+ nesStatus.className = 'status-message';
520
+
521
  const reader = new FileReader();
522
  reader.onload = (e) => {
523
+ try {
524
+ const rom = new Uint8Array(e.target.result);
525
+ nes.loadROM(rom);
526
+
527
+ // Update game info
528
+ document.getElementById('nesGameInfo').innerHTML = `
529
+ <p class="mb-2"><span class="text-purple-400">${file.name}</span></p>
530
+ <p class="mb-2">Controls:</p>
531
+ <ul class="list-disc pl-5">
532
+ <li>Arrow Keys: D-Pad</li>
533
+ <li>Z: A Button</li>
534
+ <li>X: B Button</li>
535
+ <li>Enter: Start</li>
536
+ <li>Shift: Select</li>
537
+ </ul>
538
+ <p class="mt-4 text-xs text-gray-400">Press Save State to save your progress</p>
539
+ <div class="mt-4">
540
+ <h4 class="text-lg font-bold mb-2 text-purple-400">Sample ROMs</h4>
541
+ <button id="loadSample1" class="btn-pixel text-xs px-3 py-1 mb-2">Super Mario Bros</button>
542
+ <button id="loadSample2" class="btn-pixel text-xs px-3 py-1 mb-2">Donkey Kong</button>
543
+ <button id="loadSample3" class="btn-pixel text-xs px-3 py-1">The Legend of Zelda</button>
544
+ </div>
545
+ `;
546
+
547
+ // Update status
548
+ nesStatus.textContent = `Successfully loaded ${file.name}`;
549
+ nesStatus.className = 'status-message status-success';
550
+
551
+ // Start the emulation
552
+ if (!nesInterval) {
553
+ nesInterval = setInterval(() => {
554
+ nes.frame();
555
+ }, 1000 / 60); // Target 60 FPS
556
+ }
557
+
558
+ } catch (error) {
559
+ nesStatus.textContent = `Error loading ROM: ${error.message}`;
560
+ nesStatus.className = 'status-message status-error';
561
+ console.error('Error loading ROM:', error);
562
+ }
563
  };
564
+
565
+ reader.onerror = () => {
566
+ nesStatus.textContent = 'Error reading file';
567
+ nesStatus.className = 'status-message status-error';
568
+ };
569
+
570
  reader.readAsArrayBuffer(file);
571
  });
572
 
573
+ // Sample ROMs (base64 encoded small demo ROMs)
574
+ const sampleRoms = {
575
+ mario: "BASE64_ENCODED_MARIO_ROM_DATA",
576
+ donkeykong: "BASE64_ENCODED_DONKEY_KONG_ROM_DATA",
577
+ zelda: "BASE64_ENCODED_ZELDA_ROM_DATA"
578
+ };
579
+
580
+ document.getElementById('loadSample1').addEventListener('click', () => {
581
+ // In a real implementation, you would load the sample ROM
582
+ nesStatus.textContent = "Sample ROM loading not implemented in this demo";
583
+ nesStatus.className = 'status-message status-error';
584
+ });
585
+
586
+ document.getElementById('loadSample2').addEventListener('click', () => {
587
+ nesStatus.textContent = "Sample ROM loading not implemented in this demo";
588
+ nesStatus.className = 'status-message status-error';
589
+ });
590
+
591
+ document.getElementById('loadSample3').addEventListener('click', () => {
592
+ nesStatus.textContent = "Sample ROM loading not implemented in this demo";
593
+ nesStatus.className = 'status-message status-error';
594
+ });
595
+
596
  // NES Save/Load State
597
  let nesState = null;
598
+ let nesInterval = null;
599
+ let isPaused = false;
600
 
601
  document.getElementById('nesSaveState').addEventListener('click', () => {
602
+ try {
603
+ nesState = nes.toJSON();
604
+ nesStatus.textContent = "Game state saved!";
605
+ nesStatus.className = 'status-message status-success';
606
+ } catch (error) {
607
+ nesStatus.textContent = `Error saving state: ${error.message}`;
608
+ nesStatus.className = 'status-message status-error';
609
+ }
610
  });
611
 
612
  document.getElementById('nesLoadState').addEventListener('click', () => {
613
  if (nesState) {
614
+ try {
615
+ nes.fromJSON(nesState);
616
+ nesStatus.textContent = "Game state loaded!";
617
+ nesStatus.className = 'status-message status-success';
618
+ } catch (error) {
619
+ nesStatus.textContent = `Error loading state: ${error.message}`;
620
+ nesStatus.className = 'status-message status-error';
621
+ }
622
  } else {
623
+ nesStatus.textContent = "No saved state found!";
624
+ nesStatus.className = 'status-message status-error';
625
+ }
626
+ });
627
+
628
+ // NES Pause/Resume
629
+ document.getElementById('nesPause').addEventListener('click', () => {
630
+ if (isPaused) {
631
+ // Resume
632
+ nesInterval = setInterval(() => {
633
+ nes.frame();
634
+ }, 1000 / 60);
635
+ document.getElementById('nesPause').innerHTML = '<i class="fas fa-pause mr-2"></i> Pause';
636
+ nesStatus.textContent = "Game resumed";
637
+ nesStatus.className = 'status-message status-success';
638
+ } else {
639
+ // Pause
640
+ clearInterval(nesInterval);
641
+ nesInterval = null;
642
+ document.getElementById('nesPause').innerHTML = '<i class="fas fa-play mr-2"></i> Resume';
643
+ nesStatus.textContent = "Game paused";
644
+ nesStatus.className = 'status-message';
645
  }
646
+ isPaused = !isPaused;
647
  });
648
 
649
  // NES Fullscreen
 
660
 
661
  // NES Reset
662
  document.getElementById('nesReset').addEventListener('click', () => {
663
+ if (nesInterval) {
664
+ clearInterval(nesInterval);
665
+ nesInterval = null;
666
+ }
667
+
668
  nes.reset();
669
+ nesStatus.textContent = "Game reset";
670
+ nesStatus.className = 'status-message';
671
+
672
+ // Restart emulation
673
+ nesInterval = setInterval(() => {
674
+ nes.frame();
675
+ }, 1000 / 60);
676
  });
677
 
678
  // GBA Emulator
 
857
  const gamepad = gamepads[i];
858
  if (!gamepad) continue;
859
 
860
+ // Handle gamepad input for NES
861
+ if (!nesSection.classList.contains('hidden')) {
862
+ // D-pad
863
+ if (gamepad.axes[1] < -0.5) nes.buttonDown(1, jsnes.Controller.BUTTON_UP);
864
+ else nes.buttonUp(1, jsnes.Controller.BUTTON_UP);
865
+
866
+ if (gamepad.axes[1] > 0.5) nes.buttonDown(1, jsnes.Controller.BUTTON_DOWN);
867
+ else nes.buttonUp(1, jsnes.Controller.BUTTON_DOWN);
868
+
869
+ if (gamepad.axes[0] < -0.5) nes.buttonDown(1, jsnes.Controller.BUTTON_LEFT);
870
+ else nes.buttonUp(1, jsnes.Controller.BUTTON_LEFT);
871
+
872
+ if (gamepad.axes[0] > 0.5) nes.buttonDown(1, jsnes.Controller.BUTTON_RIGHT);
873
+ else nes.buttonUp(1, jsnes.Controller.BUTTON_RIGHT);
874
+
875
+ // Buttons (standard mapping)
876
+ if (gamepad.buttons[0] && gamepad.buttons[0].pressed) nes.buttonDown(1, jsnes.Controller.BUTTON_A);
877
+ else nes.buttonUp(1, jsnes.Controller.BUTTON_A);
878
+
879
+ if (gamepad.buttons[1] && gamepad.buttons[1].pressed) nes.buttonDown(1, jsnes.Controller.BUTTON_B);
880
+ else nes.buttonUp(1, jsnes.Controller.BUTTON_B);
881
+
882
+ if (gamepad.buttons[9] && gamepad.buttons[9].pressed) nes.buttonDown(1, jsnes.Controller.BUTTON_START);
883
+ else nes.buttonUp(1, jsnes.Controller.BUTTON_START);
884
+
885
+ if (gamepad.buttons[8] && gamepad.buttons[8].pressed) nes.buttonDown(1, jsnes.Controller.BUTTON_SELECT);
886
+ else nes.buttonUp(1, jsnes.Controller.BUTTON_SELECT);
887
  }
888
 
889
+ // Handle gamepad input for GBA
890
+ // This would need to be implemented based on GBA.js's input handling
 
 
 
891
  }
892
 
893
  requestAnimationFrame(pollGamepads);