use flic::{FlicFile, RasterMut};
use openpol::{grafdat, image13h, paldat};
use sdl2::audio::{AudioCallback, AudioDevice, AudioSpecDesired};
use sdl2::event::Event;
use sdl2::pixels::{Color, PixelFormatEnum};
use sdl2::render::{Texture, WindowCanvas};
use sdl2::{EventPump, TimerSubsystem};
use std::cmp;
use std::env;
use std::fs;
use std::io::prelude::*;
use std::path;
const VERSION: &str = env!("GIT_DESCRIPTION");
fn main() -> Result<(), String> {
let mut game = Game::new()?;
game.run()
}
struct Audio {
data: Vec<u8>,
position: usize,
silence: u8,
}
impl AudioCallback for Audio {
type Channel = u8;
fn callback(&mut self, out: &mut [u8]) {
let to_buffer = cmp::min(out.len(), self.data.len() - self.position);
out[..to_buffer].copy_from_slice(&self.data[self.position..self.position + to_buffer]);
self.position += to_buffer;
if self.position == self.data.len() {
for x in out[to_buffer..].iter_mut() {
*x = self.silence;
}
}
}
}
struct Game {
data_dir: path::PathBuf,
grafdat: grafdat::Grafdat,
paldat: paldat::Paldat,
screen: image13h::Image13h,
palette: Vec<u8>,
}
impl Game {
pub fn new() -> Result<Game, String> {
let args: Vec<String> = env::args().skip(1).collect();
if args.len() != 1 {
return Err("Usage: openpol GAMEDIR".to_string());
}
let root_dir = path::Path::new(&args[0]);
let data_dir = root_dir.join("data");
Ok(Game {
data_dir: data_dir.to_path_buf(),
paldat: paldat::Paldat::load(fs::File::open(root_dir.join("pal.dat")).unwrap())
.unwrap(),
grafdat: grafdat::Grafdat::load(fs::File::open(root_dir.join("graf.dat")).unwrap())
.unwrap(),
screen: image13h::Image13h::empty_screen_sized(),
palette: vec![0; paldat::PALETTE_SIZE_IN_BYTES],
})
}
pub fn run(&mut self) -> Result<(), String> {
let sdl = sdl2::init()?;
let video = sdl.video()?;
let window = video
.window(
&format!("openpol {}", VERSION),
image13h::SCREEN_WIDTH as u32 * 2,
image13h::SCREEN_HEIGHT as u32 * 2,
)
.build()
.map_err(|e| e.to_string())?;
let mut canvas = window
.into_canvas()
.target_texture()
.present_vsync()
.build()
.map_err(|e| e.to_string())?;
canvas.set_draw_color(Color::RGB(0, 0, 0));
let mut event_pump = sdl.event_pump()?;
let texture_creator = canvas.texture_creator();
let mut texture = texture_creator
.create_texture_streaming(
PixelFormatEnum::RGB24,
image13h::SCREEN_WIDTH as u32,
image13h::SCREEN_HEIGHT as u32,
)
.map_err(|e| e.to_string())?;
let mut timer = sdl.timer()?;
let audio = sdl.audio()?;
let desired_spec = AudioSpecDesired {
freq: Some(22_050),
channels: Some(1),
samples: None,
};
let mut audio_device = audio.open_playback(None, &desired_spec, |spec| Audio {
data: Vec::new(),
position: 0,
silence: spec.silence,
})?;
audio_device.resume();
self.event_loop(
&mut event_pump,
&mut timer,
&mut canvas,
&mut texture,
&mut audio_device,
)
}
fn event_loop(
&mut self,
event_pump: &mut EventPump,
timer: &mut TimerSubsystem,
canvas: &mut WindowCanvas,
texture: &mut Texture,
audio_device: &mut AudioDevice<Audio>,
) -> Result<(), String> {
let mut intro = Intro::new(&self.data_dir)?;
let mut last_render = timer.ticks();
while intro.running() {
for event in event_pump.poll_iter() {
match event {
Event::Quit { .. } => intro.stop(audio_device),
Event::KeyDown { .. } => intro.next(audio_device),
_ => (),
}
}
let now = timer.ticks();
let dt = now - last_render;
last_render = now;
intro.update(dt, audio_device);
texture.with_lock(None, |buffer: &mut [u8], _pitch: usize| {
intro.display(buffer)
})?;
canvas.clear();
canvas.copy(&texture, None, None)?;
canvas.present();
}
let mut game_running = true;
self.palette[..].copy_from_slice(self.paldat.palette_data(2));
self.screen.blit_whole(self.grafdat.main_menu(), 0, 0);
while game_running {
for event in event_pump.poll_iter() {
match event {
Event::Quit { .. } => game_running = false,
Event::KeyDown { .. } => game_running = false,
_ => (),
}
}
texture.with_lock(None, |buffer: &mut [u8], _pitch: usize| {
image13h::indices_to_rgb(self.screen.data(), &self.palette, buffer);
})?;
canvas.clear();
canvas.copy(&texture, None, None)?;
canvas.present();
}
Ok(())
}
}
struct Intro<'a> {
flic: Option<FlicFile>,
since_last_render: u32,
flic_buffer: Vec<u8>,
flic_palette: Vec<u8>,
data_dir: &'a path::Path,
buffer_changed: bool,
current_intro: u8,
running: bool,
}
impl<'a> Intro<'a> {
pub fn new(data_dir: &'a path::Path) -> Result<Intro<'a>, String> {
Ok(Intro {
flic: None,
since_last_render: 0,
flic_buffer: vec![0; image13h::SCREEN_PIXELS],
flic_palette: vec![0; 3 * image13h::COLORS],
data_dir,
buffer_changed: false,
current_intro: 0,
running: true,
})
}
pub fn update(&mut self, ticks: u32, audio_device: &mut AudioDevice<Audio>) {
let flic = match &mut self.flic {
None => match self.current_intro {
i @ 0..=2 => {
let flic =
FlicFile::open(&self.data_dir.join(format!("S00{}.DAT", i))).unwrap();
assert_eq!(flic.width() as usize, image13h::SCREEN_WIDTH);
assert_eq!(flic.height() as usize, image13h::SCREEN_HEIGHT);
self.flic = Some(flic);
match fs::File::open(&self.data_dir.join(format!("I00{}.DAT", i))) {
Err(_) => (),
Ok(mut audio_file) => {
let mut len_buf = [0; 4];
audio_file.read_exact(&mut len_buf).unwrap();
let expected_len = u32::from_le_bytes(len_buf) as usize;
let mut audio_data = Vec::new();
audio_file.read_to_end(&mut audio_data).unwrap();
assert_eq!(audio_data.len(), expected_len);
let mut audio_lock = audio_device.lock();
audio_lock.data = audio_data;
audio_lock.position = 0;
}
};
self.flic.as_mut().unwrap()
}
_ => return,
},
Some(flic) => flic,
};
let ms_per_frame = flic.speed_msec();
self.since_last_render += ticks;
let buffer_changed = self.since_last_render >= ms_per_frame;
if buffer_changed {
self.buffer_changed = true;
let mut raster = RasterMut::new(
image13h::SCREEN_WIDTH,
image13h::SCREEN_HEIGHT,
&mut self.flic_buffer,
&mut self.flic_palette,
);
while self.since_last_render >= ms_per_frame {
let playback_result = flic.read_next_frame(&mut raster).unwrap();
if playback_result.ended {
self.next(audio_device);
return;
} else {
self.since_last_render -= ms_per_frame;
}
}
}
}
pub fn display(&mut self, buffer: &mut [u8]) {
if self.buffer_changed {
image13h::indices_to_rgb(&self.flic_buffer, &self.flic_palette, buffer);
self.buffer_changed = false;
}
}
pub fn running(&self) -> bool {
self.running && self.current_intro < 3
}
pub fn stop(&mut self, audio_device: &mut AudioDevice<Audio>) {
self.running = false;
clear_audio(audio_device);
}
pub fn next(&mut self, audio_device: &mut AudioDevice<Audio>) {
self.since_last_render = 0;
self.flic = None;
self.current_intro += 1;
clear_audio(audio_device);
}
}
fn clear_audio(audio_device: &mut AudioDevice<Audio>) {
let mut audio_lock = audio_device.lock();
audio_lock.data = Vec::new();
audio_lock.position = 0;
}