commit f84466c03adc767bba073c7e83661068007d6ed0 Author: Daniel Bulant Date: Wed Jan 25 22:56:36 2023 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ff132ad --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +frames +output.ch8 +badapple.webm \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/chip8badapple.iml b/.idea/chip8badapple.iml new file mode 100644 index 0000000..c254557 --- /dev/null +++ b/.idea/chip8badapple.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..3ce3588 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..b29033d --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..1f992b7 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,258 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bindgen" +version = "0.59.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cc" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chip8badapple" +version = "0.1.0" +dependencies = [ + "ffmpeg-next", +] + +[[package]] +name = "clang-sys" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "ffmpeg-next" +version = "5.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a80971eee67be0079a1c8890bde68226fe9bd0441740fd6ddd0cee131486b321" +dependencies = [ + "bitflags", + "ffmpeg-sys-next", + "libc", +] + +[[package]] +name = "ffmpeg-sys-next" +version = "5.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d780b36e092254367e2f1f21191992735c8e23f31a5a5a8678db3a79f775021f" +dependencies = [ + "bindgen", + "cc", + "libc", + "num_cpus", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "proc-macro2" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f679b6e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "chip8badapple" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ffmpeg-next="5.1.1" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..54f87b2 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,134 @@ +use ffmpeg_next::format::{input, Pixel}; +use ffmpeg_next::media::Type; +use ffmpeg_next::software::scaling::{flag::Flags}; +use ffmpeg_next::util::frame::video::Video; +use std::env; +use std::fs::File; +use std::io::{Seek, SeekFrom, Write}; + +const WIDTH: u8 = 64; +const HEIGHT: u8 = 32; + +fn main() -> Result<(), ffmpeg_next::Error> { + ffmpeg_next::init().unwrap(); + + if let Ok(mut ictx) = input(&env::args().nth(1).expect("Cannot open file.")) { + let input = ictx + .streams() + .best(Type::Video) + .ok_or(ffmpeg_next::Error::StreamNotFound)?; + let video_stream_index = input.index(); + + let mut output_file = File::create("output.ch8").unwrap(); + + let context_decoder = ffmpeg_next::codec::context::Context::from_parameters(input.parameters())?; + let mut decoder = context_decoder.decoder().video()?; + + // clion doesn't like use for this... + let mut scaler = ffmpeg_next::software::scaling::context::Context::get( + // input + decoder.format(), + decoder.width(), + decoder.height(), + // output + Pixel::MonoBlack, + WIDTH as u32, + HEIGHT as u32, + Flags::BILINEAR + )?; + + let mut frame_index = 0; + + // the below is the main program loop. It's simple, so it's hardcoded. + // V0=x, V1=y, V10=VA=frame count, V11=VB=delay timer + // it uses sprite bank to draw sprites on the screen + // because of frame counting, maximum length is roughly 36.4 minutes (for 30fps) + let base_rom = [ + 0xA3,0x00, // 0x200 I = 0x300 + 0x6C,0x01, // 0x202 VC = 1 + + // main print loop + 0x00,0xE0, // 0x204 clear screen, + 0x61,0x00, // 0x206 V1 = 0 + 0x60,0x00, // 0x208 V0 = 0 + 0xD0,0x1F, // 0x20A draw sprite at V0, V1 with height 15 + 0x70,0x08, // 0x20C V0 += 8 + 0x62, 120, // 0x20E V2 = 120 + 0xF2,0x1E, // 0x210 I += V2 + + 0x30, 32, // 0x212 if V0 == 32 skip + 0x12,0x0A, // 0x214 goto 0x20A + 0x71, 15, // 0x216 V1 += 15 + 0x31, 45, // 0x218 if V1 == 45 skip + 0x12,0x08, // 0x21A goto 0x208 + + // wait for next frame. Delay timer ticks every 1/60th of a second, video is 30fps, hence 2 ticks + 0x6B,0x02, // 0x21C VB = 2 + 0xFB,0x15, // 0x21E delay timer = VB + 0xFB,0x07, // 0x220 VB = delay timer + + 0x4B,0x00, // 0x222 if VB != 0 skip + 0x12,0x22, // 0x224 goto 0x222 + + 0x12,0x04, // 0x226 goto 0x204 + // unreachable, for future use + 0x12,0x28, // 0x228 goto 0x228 (self jump, our emulator exits) + 0x00,0x00 // crash if we get here + ]; + output_file.write_all(&base_rom).unwrap(); + output_file.seek(std::io::SeekFrom::Start(0x300)).unwrap(); + + let mut receive_and_process_decoded_frames = + |decoder: &mut ffmpeg_next::decoder::Video| -> Result<(), ffmpeg_next::Error> { + let mut decoded = Video::empty(); + while decoder.receive_frame(&mut decoded).is_ok() { + let mut bw_frame = Video::empty(); + scaler.run(&decoded, &mut bw_frame)?; + let data = bw_frame.data(0); + // dbg!(data); + let mut sprite_bank: [[u8; 8]; 3] = [ + [ + 0; 8 // there are 8 sprites in 64 pixel wide screen + ]; 3 // 3 parts 15 pixels each to fill 32 pixel tall screen + ]; + + for i in 0..8 { + for j in 0..3 { + sprite_bank[j][i] = data[i * 3 + j]; + } + } + + let mut file = File::create(format!("frames/f{}.pbm", frame_index)).unwrap(); + file.write_all(format!("P4\n{} {}\n", bw_frame.width(), bw_frame.height()).as_bytes()).unwrap(); + file.write_all(data).unwrap(); + + for part in 0..3 { + for sprite in 0..8 { + let mut sprite_byte = 0; + for pixel in 0..8 { + if sprite_bank[part][sprite] & 1 << (7 - pixel) != 0 { + sprite_byte |= 1 << (7 - pixel); + } + } + output_file.write_all(&[sprite_byte]).unwrap(); + } + } + + frame_index += 1; + } + Ok(()) + }; + + for (stream, packet) in ictx.packets() { + if stream.index() == video_stream_index { + + decoder.send_packet(&packet)?; + receive_and_process_decoded_frames(&mut decoder)?; + } + } + decoder.send_eof()?; + receive_and_process_decoded_frames(&mut decoder)?; + } + + Ok(()) +} \ No newline at end of file