viewer: preview reassembled UI screens in the PAK browser
When an opened GP_*.pak RATC entry is a .rat build, compose the screen (ui_layout::compose_build) and show it atop the RATC detail, labelled 'UI screen', with the sprite children below. Reuses the existing pak-open + T8aD-decode path — no new browser/threading. Entry summaries mark UI builds. cargo check -p sylpheed-viewer: clean.
This commit is contained in:
@@ -160,8 +160,9 @@ pub enum PakContent {
|
|||||||
T8ad(ImageRgba),
|
T8ad(ImageRgba),
|
||||||
/// An LSTA sprite list — inline T8aD frames.
|
/// An LSTA sprite list — inline T8aD frames.
|
||||||
Lsta(Vec<ImageRgba>),
|
Lsta(Vec<ImageRgba>),
|
||||||
/// A RATC bundle — its listed children (T8aD children carry a decoded image).
|
/// A RATC bundle — its listed children (T8aD children carry a decoded
|
||||||
Ratc(Vec<RatcEntry>),
|
/// image), plus the reassembled UI screen when the bundle is a `.rat` build.
|
||||||
|
Ratc(Vec<RatcEntry>, Option<ImageRgba>),
|
||||||
/// Plain-text / XML payload, with its encoding label.
|
/// Plain-text / XML payload, with its encoding label.
|
||||||
Text { text: String, encoding: String },
|
Text { text: String, encoding: String },
|
||||||
}
|
}
|
||||||
@@ -192,6 +193,13 @@ impl ImageRgba {
|
|||||||
rgba: img.rgba,
|
rgba: img.rgba,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn from_composed(s: sylpheed_formats::ui_layout::ComposedScreen) -> Self {
|
||||||
|
Self {
|
||||||
|
width: s.width,
|
||||||
|
height: s.height,
|
||||||
|
rgba: s.rgba,
|
||||||
|
}
|
||||||
|
}
|
||||||
fn pixels(&self) -> usize {
|
fn pixels(&self) -> usize {
|
||||||
(self.width as usize) * (self.height as usize)
|
(self.width as usize) * (self.height as usize)
|
||||||
}
|
}
|
||||||
@@ -1469,7 +1477,10 @@ fn classify_content(payload: &[u8]) -> PakContent {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
if !entries.is_empty() {
|
if !entries.is_empty() {
|
||||||
return PakContent::Ratc(entries);
|
// If this bundle is a `.rat` UI build, reassemble the screen.
|
||||||
|
let ui_screen = sylpheed_formats::ui_layout::compose_build(payload, false)
|
||||||
|
.map(ImageRgba::from_composed);
|
||||||
|
return PakContent::Ratc(entries, ui_screen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -672,7 +672,9 @@ fn draw_pak_browser(ui: &mut egui::Ui, pak: &mut PakView) {
|
|||||||
PakContent::Png(img) => draw_png_detail(ui, row, img, img_tex),
|
PakContent::Png(img) => draw_png_detail(ui, row, img, img_tex),
|
||||||
PakContent::T8ad(img) => draw_t8ad_detail(ui, row, img, img_tex),
|
PakContent::T8ad(img) => draw_t8ad_detail(ui, row, img, img_tex),
|
||||||
PakContent::Lsta(frames) => draw_lsta_detail(ui, row, frames, img_tex),
|
PakContent::Lsta(frames) => draw_lsta_detail(ui, row, frames, img_tex),
|
||||||
PakContent::Ratc(children) => draw_ratc_detail(ui, row, children, img_tex),
|
PakContent::Ratc(children, ui_screen) => {
|
||||||
|
draw_ratc_detail(ui, row, children, ui_screen.as_ref(), img_tex)
|
||||||
|
}
|
||||||
PakContent::Text { text, encoding } => {
|
PakContent::Text { text, encoding } => {
|
||||||
draw_text_detail(ui, row, text, encoding)
|
draw_text_detail(ui, row, text, encoding)
|
||||||
}
|
}
|
||||||
@@ -699,7 +701,10 @@ fn row_kind(row: &crate::iso_loader::PakRow) -> String {
|
|||||||
PakContent::Png(img) => format!("PNG {}×{}", img.width, img.height),
|
PakContent::Png(img) => format!("PNG {}×{}", img.width, img.height),
|
||||||
PakContent::T8ad(img) => format!("T8aD {}×{}", img.width, img.height),
|
PakContent::T8ad(img) => format!("T8aD {}×{}", img.width, img.height),
|
||||||
PakContent::Lsta(f) => format!("LSTA · {} sprite(s)", f.len()),
|
PakContent::Lsta(f) => format!("LSTA · {} sprite(s)", f.len()),
|
||||||
PakContent::Ratc(c) => format!("RATC · {} item(s)", c.len()),
|
PakContent::Ratc(c, screen) => {
|
||||||
|
let s = if screen.is_some() { " · UI screen" } else { "" };
|
||||||
|
format!("RATC · {} item(s){s}", c.len())
|
||||||
|
}
|
||||||
PakContent::Text { .. } => "text".into(),
|
PakContent::Text { .. } => "text".into(),
|
||||||
PakContent::None => row.identity.clone(),
|
PakContent::None => row.identity.clone(),
|
||||||
}
|
}
|
||||||
@@ -939,21 +944,47 @@ fn draw_ratc_detail(
|
|||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
row: &crate::iso_loader::PakRow,
|
row: &crate::iso_loader::PakRow,
|
||||||
children: &[crate::iso_loader::RatcEntry],
|
children: &[crate::iso_loader::RatcEntry],
|
||||||
|
ui_screen: Option<&ImageRgba>,
|
||||||
img_tex: &mut ImgCache,
|
img_tex: &mut ImgCache,
|
||||||
) {
|
) {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.heading("RATC bundle");
|
ui.heading("RATC bundle");
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.label(format!("{} item(s)", children.len()));
|
ui.label(format!("{} item(s)", children.len()));
|
||||||
|
if ui_screen.is_some() {
|
||||||
|
ui.separator();
|
||||||
|
ui.strong("🖼 UI screen");
|
||||||
|
}
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.weak("colours unverified");
|
ui.weak("colours unverified");
|
||||||
});
|
});
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
let refs: Vec<&ImageRgba> = children.iter().filter_map(|c| c.image.as_ref()).collect();
|
// Cache the reassembled screen (if any) as texture 0, then child thumbnails.
|
||||||
|
let mut refs: Vec<&ImageRgba> = Vec::new();
|
||||||
|
if let Some(s) = ui_screen {
|
||||||
|
refs.push(s);
|
||||||
|
}
|
||||||
|
let child_base = refs.len();
|
||||||
|
refs.extend(children.iter().filter_map(|c| c.image.as_ref()));
|
||||||
ensure_textures(ui, row.hash, &refs, img_tex);
|
ensure_textures(ui, row.hash, &refs, img_tex);
|
||||||
|
|
||||||
let mut img_i = 0;
|
// The reassembled screen, scaled to fit the panel width.
|
||||||
|
if ui_screen.is_some() {
|
||||||
|
if let Some(tex) = img_tex.as_ref().and_then(|(_, t)| t.first()) {
|
||||||
|
ui.label("Reassembled from this screen's .rat layout records:");
|
||||||
|
let sz = tex.size_vec2();
|
||||||
|
let scale = (ui.available_width() / sz.x.max(1.0)).min(1.0);
|
||||||
|
ui.add(egui::Image::new(egui::load::SizedTexture::new(
|
||||||
|
tex.id(),
|
||||||
|
[sz.x * scale, sz.y * scale],
|
||||||
|
)));
|
||||||
|
ui.weak("Frame/glow decorations (no .rat) and the live 3D background are omitted.");
|
||||||
|
ui.separator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut img_i = child_base;
|
||||||
for c in children {
|
for c in children {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
if c.image.is_some() {
|
if c.image.is_some() {
|
||||||
|
|||||||
Reference in New Issue
Block a user