librsmsx 0.4.6

a MSX emulator written in rust, a port from gomsx
Documentation
use super::{
    base_system::{BaseSystem, MSX_H, MSX_W1, MSX_W2},
    vdp::{SCREEN0, SCREEN1, SCREEN2},
};

use std::cell::RefCell;

use sdl3::{
    pixels::{Color, PixelFormat},
    render::{FRect, Texture},
    surface::Surface,
    sys::pixels::SDL_PixelFormat,
};

/// ARGB8888
fn color_from_hex(pixel: u32) -> Color {
    Color::from_u32(
        unsafe { &PixelFormat::from_ll(SDL_PixelFormat::ARGB8888) },
        pixel,
    )
}

#[derive(Clone, Debug, Copy)]
enum ActiveTexture {
    Tex256,
    Tex320,
}

#[derive(PartialEq)]
pub enum GraphicsType {
    None,
    Normal,
}

impl GraphicsType {
    pub fn create<'a>(self, quality: bool) -> GraphicsDriver<'a> {
        match self {
            GraphicsType::None => GraphicsDriver::None, //Rc::new(RefCell::new(NullGraphics::new(quality))),
            GraphicsType::Normal => {
                let surface256 = Surface::new(MSX_W2, MSX_H, unsafe {
                    PixelFormat::from_ll(SDL_PixelFormat::RGB24)
                })
                .unwrap();
                let surface320 = Surface::new(MSX_W1, MSX_H, unsafe {
                    PixelFormat::from_ll(SDL_PixelFormat::RGB24)
                })
                .unwrap();
                let colors = [
                    color_from_hex(0xff000000),
                    color_from_hex(0xff010101),
                    color_from_hex(0xff3eb849),
                    color_from_hex(0xff74d07d),
                    color_from_hex(0xff5955e0),
                    color_from_hex(0xff8076f1),
                    color_from_hex(0xffb95e51),
                    color_from_hex(0xff65dbef),
                    color_from_hex(0xffdb6559),
                    color_from_hex(0xffff897d),
                    color_from_hex(0xffccc35e),
                    color_from_hex(0xffded087),
                    color_from_hex(0xff3aa241),
                    color_from_hex(0xffb766b5),
                    color_from_hex(0xffcccccc),
                    color_from_hex(0xffffffff),
                ];
                let param = GraphicsDriverNormalParam {
                    quality,
                    surface256,
                    surface320,
                    active_texture: ActiveTexture::Tex256,
                    x0: 0,
                    y0: 0,
                };
                GraphicsDriver::Normal(colors, RefCell::new(param))
            }
        }
    }
}

pub struct GraphicsDriverNormalParam<'a> {
    quality: bool,
    surface256: Surface<'a>,
    surface320: Surface<'a>,
    active_texture: ActiveTexture,
    x0: i16,
    y0: i16,
}

pub enum GraphicsDriver<'a> {
    None,
    Normal([Color; 16], RefCell<GraphicsDriverNormalParam<'a>>),
}

impl GraphicsDriver<'_> {
    pub fn render(&self, sys: &mut BaseSystem) {
        match &self {
            GraphicsDriver::None => {}
            GraphicsDriver::Normal(_colors, param) => {
                let active_texture = param.borrow().active_texture;
                let x0 = param.borrow().x0 as f32;
                let y0 = param.borrow().y0 as f32;
                match active_texture {
                    ActiveTexture::Tex256 => {
                        let canvas = sys.get_canvas();
                        let texture_creator = canvas.texture_creator();
                        let mut texture =
                            Texture::from_surface(&param.borrow().surface256, &texture_creator)
                                .unwrap();
                        texture.set_scale_mode(if param.borrow().quality {
                            sdl3::render::ScaleMode::Linear
                        } else {
                            sdl3::render::ScaleMode::Nearest
                        });
                        canvas.clear();
                        canvas
                            .copy(
                                &texture,
                                None,
                                FRect {
                                    x: x0,
                                    y: y0,
                                    w: x0 + MSX_W2 as f32,
                                    h: y0 + MSX_H as f32,
                                },
                            )
                            .unwrap();
                    }
                    ActiveTexture::Tex320 => {
                        let canvas = sys.get_canvas();
                        let texture_creator = canvas.texture_creator();
                        let mut texture =
                            Texture::from_surface(&param.borrow().surface256, &texture_creator)
                                .unwrap();
                        texture.set_scale_mode(if param.borrow().quality {
                            sdl3::render::ScaleMode::Linear
                        } else {
                            sdl3::render::ScaleMode::Nearest
                        });
                        canvas.clear();
                        canvas
                            .copy(
                                &texture,
                                None,
                                FRect {
                                    x: x0,
                                    y: y0,
                                    w: x0 + MSX_W1 as f32,
                                    h: y0 + MSX_H as f32,
                                },
                            )
                            .unwrap();
                    }
                }
            }
        }
    }
    pub fn set_logical_resolution(&mut self, scr_mode: u8) {
        match &self {
            GraphicsDriver::None => {}
            GraphicsDriver::Normal(_colors, param) => match scr_mode {
                SCREEN0 => param.borrow_mut().active_texture = ActiveTexture::Tex320,
                SCREEN2 => param.borrow_mut().active_texture = ActiveTexture::Tex256,
                SCREEN1 => param.borrow_mut().active_texture = ActiveTexture::Tex256,
                _ => panic!("setLogicalResolution: mode not supported"),
            },
        }
    }
    pub fn draw_pixel(&mut self, x: u32, y: u32, color: usize) {
        match &self {
            GraphicsDriver::None => {}
            GraphicsDriver::Normal(colors, param) => {
                let active_texture = param.borrow().active_texture;
                match active_texture {
                    ActiveTexture::Tex256 => {
                        param.borrow_mut().surface256.with_lock_mut(|buffer| {
                            let offset = y as usize * 3 * MSX_W2 as usize + x as usize * 3;
                            buffer[offset] = colors[color].r;
                            buffer[offset + 1] = colors[color].g;
                            buffer[offset + 2] = colors[color].b;
                        })
                    }
                    ActiveTexture::Tex320 => {
                        param.borrow_mut().surface320.with_lock_mut(|buffer| {
                            let offset = y as usize * 3 * MSX_W1 as usize + x as usize * 3;
                            buffer[offset] = colors[color].r;
                            buffer[offset + 1] = colors[color].g;
                            buffer[offset + 2] = colors[color].b;
                        })
                    }
                }
            }
        }
    }
}