working on drag and drop to load games
This commit is contained in:
parent
435a1b41fc
commit
91f0aa479b
|
@ -7,6 +7,8 @@ Nestur is an NES emulator. There are plenty of full-featured emulators out there
|
|||
|
||||
<img src="pics/smb.png" width=250> <img src="pics/zelda_dungeon.png" width=250> <img src="pics/kirby.png" width=250> <img src="pics/dk.png" width=250> <img src="pics/smb3.png" width=250> <img src="pics/excitebike.png" width=250>
|
||||
|
||||
The code aims to follow the explanations from the [NES dev wiki](https://wiki.nesdev.com/w/index.php/NES_reference_guide) where possible, especially in the PPU, and the comments quote from it often. Thanks to everyone who contributes to that wiki/forum, and to Michael Fogleman's [NES](https://github.com/fogleman/nes) and Scott Ferguson's [Fergulator](https://github.com/scottferg/Fergulator) for getting me unstuck at several points.
|
||||
|
||||
## Controls
|
||||
```
|
||||
Button | Key
|
||||
|
@ -25,8 +27,6 @@ ___________________
|
|||
## Save states
|
||||
To save the state of the game at any time, press F5. To load the most recent save state, press F9. If the game is called `mygame.nes`, the save state files will be called `mygame-X.dat` where `X` is a number. To load any previous save state, drag and drop a `.dat` file onto the window.
|
||||
|
||||
The code aims to follow the explanations from the [NES dev wiki](https://wiki.nesdev.com/w/index.php/NES_reference_guide) where possible, especially in the PPU, and the comments quote from it often. Thanks to everyone who contributes to that wiki/forum, and to Michael Fogleman's [NES](https://github.com/fogleman/nes) and Scott Ferguson's [Fergulator](https://github.com/scottferg/Fergulator) for getting me unstuck at several points.
|
||||
|
||||
## Compilation and use
|
||||
|
||||
1. Install [Rust](https://www.rust-lang.org/tools/install)
|
||||
|
|
51
src/main.rs
51
src/main.rs
|
@ -18,10 +18,15 @@ use state::{save_state, load_state, find_next_filename, find_last_filename};
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Instant, Duration};
|
||||
|
||||
use sdl2::Sdl;
|
||||
use sdl2::render::{Canvas, Texture};
|
||||
use sdl2::keyboard::Keycode;
|
||||
use sdl2::EventPump;
|
||||
use sdl2::event::Event;
|
||||
use sdl2::pixels::PixelFormatEnum;
|
||||
use sdl2::video::Window;
|
||||
use sdl2::messagebox::*;
|
||||
|
||||
// use cpuprofiler::PROFILER;
|
||||
|
||||
|
@ -37,18 +42,48 @@ fn main() -> Result<(), String> {
|
|||
let byte_height = 240 * screen::SCALE_FACTOR; // NES image is 240 pixels tall, multiply by scale factor for total number of rows needed
|
||||
let mut screen_buffer = vec![0; byte_width * byte_height]; // contains raw RGB data for the screen
|
||||
|
||||
let argv = std::env::args().collect::<Vec<String>>();
|
||||
let filename = if argv.len() > 1 {
|
||||
argv[1].to_string()
|
||||
} else {
|
||||
show_simple_message_box(MessageBoxFlag::OK, title: &str, message: &str, window: W)
|
||||
let name;
|
||||
'waiting: loop {
|
||||
for event in event_pump.poll_iter() {
|
||||
match event {
|
||||
Event::DropFile{ filename: f, .. } => {
|
||||
name = f;
|
||||
break 'waiting;
|
||||
},
|
||||
_ => std::thread::sleep(Duration::from_millis(500)),
|
||||
}
|
||||
}
|
||||
}
|
||||
name
|
||||
};
|
||||
run_game(&sdl_context, &mut event_pump, &mut screen_buffer, &mut canvas, &mut texture, &filename)
|
||||
}
|
||||
|
||||
fn run_game(
|
||||
sdl_context: &Sdl,
|
||||
event_pump: &mut EventPump,
|
||||
screen_buffer: &mut Vec<u8>,
|
||||
canvas: &mut Canvas<Window>,
|
||||
texture: &mut Texture,
|
||||
filename: &str
|
||||
) -> Result<(), String> {
|
||||
|
||||
// Set up audio
|
||||
let mut temp_buffer = vec![]; // receives one sample each time the APU ticks. this is a staging buffer so we don't have to lock the mutex too much.
|
||||
let apu_buffer = Arc::new(Mutex::new(Vec::<f32>::new())); // stays in this thread, receives raw samples between frames
|
||||
let sdl_buffer = Arc::clone(&apu_buffer); // used in audio device's callback to select the samples it needs
|
||||
let audio_device = audio::initialize(&sdl_context, sdl_buffer).expect("Could not create audio device");
|
||||
let audio_device = audio::initialize(sdl_context, sdl_buffer).expect("Could not create audio device");
|
||||
let mut half_cycle = false;
|
||||
audio_device.resume();
|
||||
|
||||
// Initialize hardware components
|
||||
let filename = get_filename();
|
||||
let filepath = Path::new(&filename).to_path_buf();
|
||||
let mapper = get_mapper(filename.clone());
|
||||
let filepath = Path::new(filename).to_path_buf();
|
||||
let mapper = get_mapper(filename.to_string());
|
||||
let ppu = Ppu::new(mapper.clone());
|
||||
let apu = Apu::new();
|
||||
let mut cpu = Cpu::new(mapper.clone(), ppu, apu);
|
||||
|
@ -79,12 +114,12 @@ fn main() -> Result<(), String> {
|
|||
for _ in 0..cpu_cycles * 3 {
|
||||
let (pixel, end_of_frame) = cpu.ppu.clock();
|
||||
match pixel {
|
||||
Some((x, y, color)) => draw_pixel(&mut screen_buffer, x, y, color),
|
||||
Some((x, y, color)) => draw_pixel(screen_buffer, x, y, color),
|
||||
None => (),
|
||||
};
|
||||
if end_of_frame {
|
||||
fps += 1; // keep track of how many frames we've rendered this second
|
||||
draw_to_window(&mut texture, &mut canvas, &screen_buffer)?; // draw the buffer to the window with SDL
|
||||
draw_to_window(texture, canvas, &screen_buffer)?; // draw the buffer to the window with SDL
|
||||
let mut b = apu_buffer.lock().unwrap(); // unlock mutex to the real buffer
|
||||
b.append(&mut temp_buffer); // send this frame's audio data, emptying the temp buffer
|
||||
let now = Instant::now();
|
||||
|
@ -93,7 +128,7 @@ fn main() -> Result<(), String> {
|
|||
std::thread::sleep(timer + Duration::from_millis(1000/60) - now);
|
||||
}
|
||||
timer = Instant::now();
|
||||
if !process_events(&mut event_pump, &filepath, &mut cpu) {
|
||||
if !process_events(event_pump, &filepath, &mut cpu) {
|
||||
break 'running;
|
||||
}
|
||||
}
|
||||
|
@ -162,7 +197,7 @@ TODO:
|
|||
- high- and low-pass audio filters
|
||||
- DMC audio channel
|
||||
- untangle CPU and APU/PPU?
|
||||
- GUI? drag and drop ROMs?
|
||||
- GUI: load new game if .nes dropped, instructions on screen if no arg given, error messages if wrong file dropped.
|
||||
- reset function/button
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue