gba.rsannotatedgba.rssource142 lines · 4.3 KB · raw
1use crate::memviewer::MemoryViewerState;
2use crate::{DebugRenderContext, DebugRenderFn, memviewer};
3use egui::{Panel, UiKind, Vec2, Window};
4use gba_core::api::GameBoyAdvanceEmulator;
5use gba_core::api::debug::GbaMemoryArea;
6use jgenesis_common::debug::Endian;
7use jgenesis_common::frontend::Color;
8use std::collections::HashMap;
9
10// Manually position windows so they don't spawn over the top panel
11const BG_PALETTES_DEFAULT_POS: [f32; 2] = [16.0, 38.0];
12const SPRITE_PALETTES_DEFAULT_POS: [f32; 2] = [396.0, 38.0];
13
14struct PaletteWindowState {
15    open: bool,
16    offset: usize,
17    texture: Option<egui::TextureId>,
18}
19
20impl PaletteWindowState {
21    fn new_bg() -> Self {
22        Self { open: true, offset: 0, texture: None }
23    }
24
25    fn new_obj() -> Self {
26        Self { open: true, offset: 256, texture: None }
27    }
28}
29
30struct State {
31    memory_viewer_states: HashMap<GbaMemoryArea, MemoryViewerState>,
32    bg_palette: PaletteWindowState,
33    obj_palette: PaletteWindowState,
34    palette_buffer: Box<[Color; 512]>,
35}
36
37impl State {
38    fn new() -> Self {
39        let memory_viewer_states = GbaMemoryArea::ALL
40            .into_iter()
41            .map(|area| (area, MemoryViewerState::new(area.name(), Endian::Little)))
42            .collect();
43
44        Self {
45            memory_viewer_states,
46            bg_palette: PaletteWindowState::new_bg(),
47            obj_palette: PaletteWindowState::new_obj(),
48            palette_buffer: vec![Color::default(); 512].into_boxed_slice().try_into().unwrap(),
49        }
50    }
51}
52
53#[must_use]
54pub fn render_fn() -> Box<DebugRenderFn<GameBoyAdvanceEmulator>> {
55    let mut state = State::new();
56    Box::new(move |ctx, emulator| render(ctx, emulator, &mut state))
57}
58
59fn render(ctx: DebugRenderContext<'_>, emulator: &mut GameBoyAdvanceEmulator, state: &mut State) {
60    Panel::top("gba_debug_top").show_inside(ctx.egui_ui, |ui| {
61        egui::MenuBar::new().ui(ui, |ui| {
62            ui.menu_button("Memory Viewers", |ui| {
63                for area in GbaMemoryArea::ALL {
64                    if area == GbaMemoryArea::CartridgeRom {
65                        continue;
66                    }
67
68                    if ui.button(area.name()).clicked()
69                        && let Some(memviewer_state) = state.memory_viewer_states.get_mut(&area)
70                    {
71                        memviewer_state.open_window(ui);
72                        ui.close_kind(UiKind::Menu);
73                    }
74                }
75            });
76
77            ui.menu_button("Video Memory", |ui| {
78                if ui.button("BG Palettes").clicked() {
79                    state.bg_palette.open = true;
80                    ui.close_kind(UiKind::Menu);
81                }
82
83                if ui.button("Sprite Palettes").clicked() {
84                    state.obj_palette.open = true;
85                    ui.close_kind(UiKind::Menu);
86                }
87            });
88        });
89    });
90
91    for area in GbaMemoryArea::ALL {
92        let Some(state) = state.memory_viewer_states.get_mut(&area) else { continue };
93
94        let mut memory = emulator.debug().memory_view(area);
95        memviewer::render(ctx.egui_ui, memory.as_mut(), state);
96    }
97
98    emulator.debug().copy_palette_ram(state.palette_buffer.as_mut_slice());
99
100    render_palette_window(
101        ctx.egui_ui,
102        "BG Palettes",
103        BG_PALETTES_DEFAULT_POS,
104        state.palette_buffer.as_slice(),
105        &mut state.bg_palette,
106    );
107    render_palette_window(
108        ctx.egui_ui,
109        "Sprite Palettes",
110        SPRITE_PALETTES_DEFAULT_POS,
111        state.palette_buffer.as_slice(),
112        &mut state.obj_palette,
113    );
114}
115
116fn render_palette_window(
117    ctx: &egui::Context,
118    title: &str,
119    default_pos: [f32; 2],
120    buffer: &[Color],
121    state: &mut PaletteWindowState,
122) {
123    Window::new(title)
124        .open(&mut state.open)
125        .default_pos(default_pos)
126        .default_size([350.0, 400.0])
127        .show(ctx, |ui| {
128            let texture = crate::update_egui_texture(
129                ctx,
130                [16, 16],
131                &buffer[state.offset..state.offset + 256],
132                &mut state.texture,
133            );
134
135            let mut size = ui.available_width();
136            if ui.available_height() < size {
137                size = ui.available_height();
138            }
139
140            ui.image((texture, Vec2::new(size, size)));
141        });
142}