Skip to content

Commit

Permalink
Add zip support
Browse files Browse the repository at this point in the history
  • Loading branch information
aelred committed Sep 22, 2024
1 parent a625e77 commit 47887bd
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 23 deletions.
63 changes: 63 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ crate-type = ["cdylib", "rlib"]
[features]
default = ["sdl", "web"]
sdl = ["dep:sdl2", "dep:env_logger"]
web = ["dep:wasm-bindgen", "dep:web-sys", "dep:console_log"]
web = ["dep:wasm-bindgen", "dep:web-sys", "dep:zip", "dep:console_log"]

[dependencies]
log = "0.4.22"
Expand Down Expand Up @@ -41,6 +41,9 @@ web-sys = { version = "0.3.70", optional = true, features = [
'DataTransferItemList',
'File',
] }
zip = { version = "2.2.0", optional = true, default-features = false, features = [
"deflate",
] }
console_log = { version = "1.0.0", optional = true }

[dev-dependencies]
Expand Down
83 changes: 61 additions & 22 deletions src/runtime/web.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
#![allow(dead_code)] // Might be disabled by features
use crate::{runtime::Runtime, BufferDisplay, Buttons, INes, HEIGHT, NES, WIDTH};
use anyhow::{anyhow, Context};
use std::{cell::RefCell, error::Error, rc::Rc};
use std::{
cell::RefCell,
error::Error,
io::{Cursor, Read},
rc::Rc,
};
use wasm_bindgen::{convert::FromWasmAbi, prelude::*, Clamped};
use web_sys::{
js_sys::{ArrayBuffer, Uint8Array},
CanvasRenderingContext2d, DragEvent, HtmlCanvasElement, ImageData, KeyboardEvent, Window,
};
use zip::ZipArchive;

const ROM: &[u8] = include_bytes!("../../roms/AlwasAwakening_demo.nes");
const MS_PER_FRAME: f64 = 1000.0 / 60.0;

pub struct Web;
Expand All @@ -18,26 +23,33 @@ impl Runtime for Web {
console_log::init_with_level(log::Level::Debug)
.map_err(|_| anyhow!("Failed to initialize logger"))?;

let ines = INes::read(ROM)?;
let cartridge = ines.into_cartridge();
let display = BufferDisplay::default();
let nes = Rc::new(RefCell::new(NES::new(cartridge, display)));
let base_nes = Rc::new(RefCell::new(Option::<NES<BufferDisplay>>::None));

let nesdown = nes.clone();
let nes = base_nes.clone();
add_event_listener("keydown", move |event: KeyboardEvent| {
let mut nes = nes.borrow_mut();
let nes = match &mut *nes {
Some(nes) => nes,
None => return Ok(()),
};
let button = keycode_binding(&event.code());
nesdown.borrow_mut().controller().press(button);
nes.controller().press(button);
Ok(())
})?;

let nesup = nes.clone();
let nes = base_nes.clone();
add_event_listener("keyup", move |event: KeyboardEvent| {
let mut nes = nes.borrow_mut();
let nes = match &mut *nes {
Some(nes) => nes,
None => return Ok(()),
};
let button = keycode_binding(&event.code());
nesup.borrow_mut().controller().release(button);
nes.controller().release(button);
Ok(())
})?;

let nesdrop = nes.clone();
let nes = base_nes.clone();
add_event_listener("drop", move |event: DragEvent| {
event.prevent_default();
let items = event.data_transfer().context("No data transfered")?.items();
Expand All @@ -48,23 +60,44 @@ impl Runtime for Web {
.get_as_file()
.map_err(|_| anyhow!("Failed to get file"))?
{
let nesread = nesdrop.clone();
let filename = file.name();
let nes = nes.clone();

let success = closure(move |array_buffer: JsValue| {
let array_buffer = array_buffer
.dyn_into::<ArrayBuffer>()
.map_err(|_| anyhow!("Failed to convert to ArrayBuffer"))?;

let array = Uint8Array::new(&array_buffer);
let mut rom = vec![0; array.length() as usize];
array.copy_to(&mut rom);
let mut data = vec![0; array.length() as usize];
array.copy_to(&mut data);

let mut rom: Option<Vec<u8>> = None;

if filename.ends_with(".zip") {
let mut zip = ZipArchive::new(Cursor::new(data))?;

for index in 0..zip.len() {
let mut file = zip.by_index(index)?;
if file.name().ends_with(".nes") {
let mut rom_data = vec![0; file.size() as usize];
file.read_exact(&mut rom_data)?;
rom = Some(rom_data);
break;
}
}
} else {
rom = Some(data);
}

let rom = rom.ok_or_else(|| anyhow!("No .nes file found"))?;

let ines = INes::read(&mut rom.as_slice())?;
let cartridge = ines.into_cartridge();
let display = BufferDisplay::default();
let nes_new = NES::new(cartridge, display);

nesread.replace(nes_new);
nes.replace(Some(nes_new));
Ok(())
});
let failure = closure(move |_| {
Expand Down Expand Up @@ -93,17 +126,22 @@ impl Runtime for Web {

let mut timestamp_last_frame_ms = 0.0;

*g.borrow_mut() = Some(Closure::new(move |timestamp_ms: f64| {
request_animation_frame(f.borrow().as_ref().unwrap())
.expect("failed to request animation frame");
let nes = base_nes.clone();
*g.borrow_mut() = Some(closure(move |timestamp_ms: f64| {
request_animation_frame(f.borrow().as_ref().unwrap())?;

if timestamp_ms - timestamp_last_frame_ms < MS_PER_FRAME {
return;
return Ok(());
}
timestamp_last_frame_ms = timestamp_ms;

// Run NES until frame starts
let mut nes = nes.borrow_mut();
let nes = match &mut *nes {
Some(nes) => nes,
None => return Ok(()),
};

// Run NES until frame starts
while nes.display().vblank() {
nes.tick();
}
Expand All @@ -117,10 +155,11 @@ impl Runtime for Web {
WIDTH as u32,
HEIGHT as u32,
)
.expect("failed to create image data");
.map_err(|_| anyhow!("Failed to create image data"))?;
context
.put_image_data(&image_data, 0.0, 0.0)
.expect("failed to put image data");
.map_err(|_| anyhow!("Failed to put image data"))?;
Ok(())
}));

request_animation_frame(g.borrow().as_ref().unwrap())?;
Expand Down

0 comments on commit 47887bd

Please sign in to comment.