msg_tool\scripts\softpal\img\pgd/
pgd3.rs

1use super::base::*;
2use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::img::*;
6use crate::utils::struct_pack::*;
7use anyhow::Result;
8use std::io::{Read, Seek};
9
10#[derive(Debug)]
11pub struct Pgd3Builder {}
12
13impl Pgd3Builder {
14    pub fn new() -> Self {
15        Self {}
16    }
17}
18
19impl ScriptBuilder for Pgd3Builder {
20    fn default_encoding(&self) -> Encoding {
21        Encoding::Cp932
22    }
23
24    fn build_script(
25        &self,
26        buf: Vec<u8>,
27        filename: &str,
28        encoding: Encoding,
29        _archive_encoding: Encoding,
30        config: &ExtraConfig,
31        archive: Option<&Box<dyn Script>>,
32    ) -> Result<Box<dyn Script>> {
33        Ok(Box::new(Pgd3::new(
34            MemReader::new(buf),
35            filename,
36            encoding,
37            config,
38            archive,
39        )?))
40    }
41
42    fn extensions(&self) -> &'static [&'static str] {
43        &["pgd"]
44    }
45
46    fn script_type(&self) -> &'static ScriptType {
47        &ScriptType::SoftpalPgd3
48    }
49
50    fn is_image(&self) -> bool {
51        true
52    }
53
54    fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
55        if buf_len >= 4 && (buf.starts_with(b"PGD3") || buf.starts_with(b"PGD2")) {
56            return Some(20);
57        }
58        None
59    }
60}
61
62#[derive(Debug)]
63pub struct Pgd3 {
64    header: PgdDiffHeader,
65    base_header: PgdGeHeader,
66    base: ImageData,
67    diff: ImageData,
68    fake_compress: bool,
69}
70
71impl Pgd3 {
72    pub fn new<R: Read + Seek>(
73        mut reader: R,
74        filename: &str,
75        encoding: Encoding,
76        config: &ExtraConfig,
77        archive: Option<&Box<dyn Script>>,
78    ) -> Result<Self> {
79        let mut sig = [0u8; 4];
80        reader.read_exact(&mut sig)?;
81        if &sig != b"PGD3" && &sig != b"PGD2" {
82            return Err(anyhow::anyhow!("Not a valid PGD3/PGD2 file"));
83        }
84        let header = PgdDiffHeader::unpack(&mut reader, false, encoding)?;
85        let diff = PgdReader::with_diff_header(reader, &header)?.unpack_overlay()?;
86        let base: Vec<u8> = if let Some(archive) = archive {
87            let mut file = archive.open_file_by_name(&header.base_name, true)?;
88            file.data()?
89        } else {
90            let path = {
91                let mut pb = std::path::PathBuf::from(filename);
92                pb.set_file_name(&header.base_name);
93                crate::utils::files::get_ignorecase_path(&pb)?
94            };
95            crate::utils::files::read_file(&path).map_err(|e| {
96                anyhow::anyhow!("Failed to read base image file '{}': {}", path.display(), e)
97            })?
98        };
99        let mut reader = MemReader::new(base);
100        reader.read_exact(&mut sig)?;
101        if &sig != b"GE \0" {
102            return Err(anyhow::anyhow!(
103                "Base image file '{}' is not a valid GE file",
104                header.base_name
105            ));
106        }
107        let base_header = PgdGeHeader::unpack(&mut reader, false, encoding)?;
108        let base = PgdReader::with_ge_header(reader, &base_header)?.unpack_ge()?;
109        Ok(Self {
110            header,
111            base_header,
112            base,
113            diff,
114            fake_compress: config.pgd_fake_compress,
115        })
116    }
117}
118
119impl Script for Pgd3 {
120    fn default_output_script_type(&self) -> OutputScriptType {
121        OutputScriptType::Json
122    }
123
124    fn default_format_type(&self) -> FormatOptions {
125        FormatOptions::None
126    }
127
128    fn is_image(&self) -> bool {
129        true
130    }
131
132    fn export_image(&self) -> Result<ImageData> {
133        let mut base = if self.base_header.is_base_file() {
134            self.base.clone()
135        } else {
136            draw_on_canvas(
137                self.base.clone(),
138                self.base_header.canvas_width,
139                self.base_header.canvas_height,
140                self.base_header.offset_x,
141                self.base_header.offset_y,
142            )?
143        };
144        draw_on_img(
145            &mut base,
146            &self.diff,
147            self.header.offset_x as u32,
148            self.header.offset_y as u32,
149        )?;
150        Ok(base)
151    }
152
153    fn import_image<'a>(
154        &'a self,
155        data: ImageData,
156        mut file: Box<dyn WriteSeek + 'a>,
157    ) -> Result<()> {
158        let mut header = PgdGeHeader {
159            offset_x: self.base_header.offset_x,
160            offset_y: self.base_header.offset_y,
161            width: self.base_header.width,
162            height: self.base_header.height,
163            canvas_height: self.base_header.canvas_height,
164            canvas_width: self.base_header.canvas_width,
165            mode: self.base_header.mode,
166            _unk: self.base_header._unk,
167        };
168        if data.height != header.height {
169            return Err(anyhow::anyhow!(
170                "Image height does not match: expected {}, got {}",
171                header.height,
172                data.height
173            ));
174        }
175        if data.width != header.width {
176            return Err(anyhow::anyhow!(
177                "Image width does not match: expected {}, got {}",
178                header.width,
179                data.width
180            ));
181        }
182        header.mode = 3;
183        file.write_all(b"GE \0")?;
184        header.pack(&mut file, false, Encoding::Utf8)?;
185        PgdWriter::new(data, self.fake_compress)
186            .with_method(3)
187            .pack_ge(&mut file)?;
188        Ok(())
189    }
190}
191
192fn draw_on_img(base: &mut ImageData, diff: &ImageData, left: u32, top: u32) -> Result<()> {
193    if base.color_type != diff.color_type {
194        return Err(anyhow::anyhow!(
195            "Color types do not match: {:?} vs {:?}",
196            base.color_type,
197            diff.color_type
198        ));
199    }
200    let bpp = base.color_type.bpp(1) as usize;
201    let base_stride = base.width as usize * bpp;
202    let diff_stride = diff.width as usize * bpp;
203
204    for y in 0..diff.height {
205        let base_y = top + y;
206        if base_y >= base.height {
207            continue; // Skip if the base image is not tall enough
208        }
209
210        for x in 0..diff.width {
211            let base_x = left + x;
212            if base_x >= base.width {
213                continue; // Skip if the base image is not wide enough
214            }
215
216            let base_index = (base_y as usize * base_stride) + (base_x as usize * bpp);
217            let diff_index = (y as usize * diff_stride) + (x as usize * bpp);
218
219            let diff_pixel = &diff.data[diff_index..diff_index + bpp];
220            let base_pixel_orig = base.data[base_index..base_index + bpp].to_vec();
221            let mut b = base_pixel_orig[0];
222            let mut g = base_pixel_orig[1];
223            let mut r = base_pixel_orig[2];
224            b ^= diff_pixel[0];
225            g ^= diff_pixel[1];
226            r ^= diff_pixel[2];
227            base.data[base_index] = b;
228            base.data[base_index + 1] = g;
229            base.data[base_index + 2] = r;
230            if bpp == 4 {
231                let mut a = base_pixel_orig[3];
232                a ^= diff_pixel[3];
233                base.data[base_index + 3] = a;
234            }
235        }
236    }
237    Ok(())
238}