diff --git a/crates/create/src/lib.rs b/crates/create/src/lib.rs index a1ab8b64e0..ee40a15f7e 100644 --- a/crates/create/src/lib.rs +++ b/crates/create/src/lib.rs @@ -562,7 +562,11 @@ fn pcx(name: &str, bytes: &[u8]) -> Result { // is black/white and an indexed file without its VGA palette is interpreted as gray. // The shared reader owns all RLE, plane unpacking and palette application. let has_vga = bytes.len().checked_sub(769).is_some_and(|at| at >= 128 && bytes.get(at) == Some(&0x0C)); - let normalize = (bits == 1 && planes == 1) || (bits == 8 && planes == 1 && !has_vga); + // The pinned decoder predates shared version-4 recognition. Its version byte is + // only a header acceptance check, not a switch for pixel or palette semantics. + // Keep this app's existing version-4 acceptance without replacing the shared pin. + let version_four = byte(1) == 4; + let normalize = version_four || (bits == 1 && planes == 1) || (bits == 8 && planes == 1 && !has_vga); let mut input = std::borrow::Cow::Borrowed(bytes); if normalize { // Normalization must not add an unbounded copy of hostile compressed input. @@ -572,11 +576,18 @@ fn pcx(name: &str, bytes: &[u8]) -> Result { let mut copy = Vec::new(); copy.try_reserve_exact(bytes.len()).map_err(|_| bad("not enough memory for this image"))?; copy.extend_from_slice(bytes); - if bits == 1 { + if version_four && let Some(version) = copy.get_mut(1) { + *version = 5; + } + if bits == 1 && planes == 1 { if let Some(palette) = copy.get_mut(16..22) { palette.copy_from_slice(&[0, 0, 0, 255, 255, 255]); } - } else if let Some(info) = copy.get_mut(68..70) { + } else if bits == 8 + && planes == 1 + && !has_vga + && let Some(info) = copy.get_mut(68..70) + { info.copy_from_slice(&2u16.to_le_bytes()); } input = std::borrow::Cow::Owned(copy); diff --git a/crates/create/src/tests.rs b/crates/create/src/tests.rs index fb4ffcc513..2dd053cb09 100644 --- a/crates/create/src/tests.rs +++ b/crates/create/src/tests.rs @@ -3144,3 +3144,16 @@ fn shared_pcx_rejects_zero_length_runs_and_oversized_planes() { let rgba = pcx_header(5, 8, 4, 1, 1, 1); assert!(super::pcx("rgba.pcx", &rgba).is_err(), "do not silently expand accepted layouts"); } + +#[test] +fn shared_pcx_keeps_version_four_rgb_and_palette_pixels() { + let mut rgb = pcx_header(4, 8, 3, 1, 1, 1); + rgb.extend_from_slice(&[10, 20, 30]); + assert!(super::is_image(&rgb)); + assert_eq!(super::pcx("windows.pcx", &rgb).unwrap().data, [10, 20, 30]); + let mut palette = pcx_header(4, 1, 2, 1, 1, 1); + palette[25..28].copy_from_slice(&[4, 5, 6]); + palette.extend_from_slice(&[0x80, 0x80]); + assert_eq!(super::pcx("windows.pcx", &palette).unwrap().data, [4, 5, 6]); + assert!(from_images(&[("windows.pcx".into(), rgb)]).is_ok()); +}