libtlg_rs/
load_tlg.rs

1use crate::stream::*;
2use crate::tvpgl::*;
3use crate::*;
4use overf::wrapping;
5use std::io::SeekFrom;
6
7fn load_tlg5<T: Read + Seek>(src: &mut T) -> Result<Tlg> {
8    let colors = src.read_u8()?;
9    let width = src.read_u32()?;
10    let height = src.read_u32()?;
11    let blockheight = src.read_u32()?;
12    let color = match colors {
13        3 => TlgColorType::Bgr24,
14        4 => TlgColorType::Bgra32,
15        1 => TlgColorType::Grayscale8,
16        _ => return Err(TlgError::UnsupportedColorType(colors)),
17    };
18    let blockcount = ((height - 1) / blockheight) + 1;
19    src.seek_relative(blockcount as i64 * 4)?;
20    let stride = width as usize * colors as usize;
21    let mut output_data = vec![0u8; width as usize * height as usize * colors as usize];
22    let mut text = [0u8; 4096];
23    let mut inbuf = vec![0u8; blockheight as usize * width as usize + 10];
24    let mut outbuf = vec![vec![0u8; blockheight as usize * width as usize + 10]; colors as usize];
25    let mut prevline: Option<Vec<u8>> = None;
26    let mut r = 0;
27    for y_blk in (0..height).step_by(blockheight as usize) {
28        for c in 0..colors {
29            let mark = src.read_u8()?;
30            let size = src.read_u32()?;
31            if mark == 0 {
32                src.read_exact(&mut inbuf[..size as usize])?;
33                r = tlg5_decompress_slide(
34                    &mut outbuf[c as usize],
35                    &inbuf[..size as usize],
36                    size as usize,
37                    &mut text,
38                    r,
39                );
40            } else {
41                src.read_exact(&mut outbuf[c as usize][..size as usize])?;
42            }
43        }
44        let y_lim = (y_blk + blockheight).min(height);
45        let mut outbufp = Vec::new();
46        for c in 0..colors {
47            outbufp.push(outbuf[c as usize].as_slice());
48        }
49        for y in y_blk..y_lim {
50            let current = &mut output_data[(y as usize * stride)..(y as usize * stride + stride)];
51            match prevline.take() {
52                Some(prev) => match color {
53                    TlgColorType::Bgr24 => {
54                        tlg5_compose_colors3(current, &prev, &outbufp, width);
55                        outbufp[0] = &outbufp[0][width as usize..];
56                        outbufp[1] = &outbufp[1][width as usize..];
57                        outbufp[2] = &outbufp[2][width as usize..];
58                    }
59                    TlgColorType::Bgra32 => {
60                        tlg5_compose_colors4(current, &prev, &outbufp, width);
61                        outbufp[0] = &outbufp[0][width as usize..];
62                        outbufp[1] = &outbufp[1][width as usize..];
63                        outbufp[2] = &outbufp[2][width as usize..];
64                        outbufp[3] = &outbufp[3][width as usize..];
65                    }
66                    TlgColorType::Grayscale8 => {
67                        tlg5_compose_colors1(current, &prev, &outbufp, width);
68                        outbufp[0] = &outbufp[0][width as usize..];
69                    }
70                },
71                None => match color {
72                    TlgColorType::Bgra32 => {
73                        let mut current_pos = 0usize;
74                        let mut pr = 0u8;
75                        let mut pg = 0u8;
76                        let mut pb = 0u8;
77                        let mut pa = 0u8;
78                        for x in 0..width as usize {
79                            let mut b = outbufp[0][x];
80                            let g = outbufp[1][x];
81                            let mut r = outbufp[2][x];
82                            let a = outbufp[3][x];
83                            wrapping! {
84                                b += g;
85                                r += g;
86                                pb += b;
87                                pg += g;
88                                pr += r;
89                                pa += a;
90                            }
91                            current[current_pos] = pb;
92                            current_pos += 1;
93                            current[current_pos] = pg;
94                            current_pos += 1;
95                            current[current_pos] = pr;
96                            current_pos += 1;
97                            current[current_pos] = pa;
98                            current_pos += 1;
99                        }
100                        outbufp[0] = &outbufp[0][width as usize..];
101                        outbufp[1] = &outbufp[1][width as usize..];
102                        outbufp[2] = &outbufp[2][width as usize..];
103                        outbufp[3] = &outbufp[3][width as usize..];
104                    }
105                    TlgColorType::Bgr24 => {
106                        let mut current_pos = 0usize;
107                        let mut pr = 0u8;
108                        let mut pg = 0u8;
109                        let mut pb = 0u8;
110                        for x in 0..width as usize {
111                            let mut b = outbufp[0][x];
112                            let g = outbufp[1][x];
113                            let mut r = outbufp[2][x];
114                            wrapping! {
115                                b += g;
116                                r += g;
117                                pb += b;
118                                pg += g;
119                                pr += r;
120                            }
121                            current[current_pos] = pb;
122                            current_pos += 1;
123                            current[current_pos] = pg;
124                            current_pos += 1;
125                            current[current_pos] = pr;
126                            current_pos += 1;
127                        }
128                        outbufp[0] = &outbufp[0][width as usize..];
129                        outbufp[1] = &outbufp[1][width as usize..];
130                        outbufp[2] = &outbufp[2][width as usize..];
131                    }
132                    TlgColorType::Grayscale8 => {
133                        let mut current_pos = 0usize;
134                        let mut pb = 0u8;
135                        for x in 0..width as usize {
136                            let b = outbufp[0][x];
137                            wrapping! {
138                                pb += b;
139                            }
140                            current[current_pos] = pb;
141                            current_pos += 1;
142                        }
143                        outbufp[0] = &outbufp[0][width as usize..];
144                    }
145                },
146            }
147            prevline = Some(current.to_vec());
148        }
149    }
150    Ok(Tlg {
151        tags: Default::default(),
152        version: 5,
153        width,
154        height,
155        color,
156        data: output_data,
157    })
158}
159
160fn load_tlg6<T: Read + Seek>(src: &mut T) -> Result<Tlg> {
161    let mut buf = [0u8; 4];
162    src.read_exact(&mut buf)?;
163    let colors = buf[0];
164    let color_type = match colors {
165        3 => TlgColorType::Bgr24,
166        4 => TlgColorType::Bgra32,
167        1 => TlgColorType::Grayscale8,
168        _ => return Err(TlgError::UnsupportedColorType(colors)),
169    };
170    if buf[1] != 0 {
171        return Err(TlgError::Str("Data flags must be 0".to_string()));
172    }
173    if buf[2] != 0 {
174        return Err(TlgError::Str("Color types must be 0".to_string()));
175    }
176    if buf[3] != 0 {
177        return Err(TlgError::Str(
178            "External golomb bit length table is not yet supported.".to_string(),
179        ));
180    }
181    let width = src.read_u32()?;
182    let height = src.read_u32()?;
183    let max_bit_length = src.read_u32()?;
184    let x_block_count = (width - 1) / (TLG6_W_BLOCK_SIZE as u32) + 1;
185    let y_block_count = (height - 1) / (TLG6_H_BLOCK_SIZE as u32) + 1;
186    let main_count = width / (TLG6_W_BLOCK_SIZE as u32);
187    let fraction = width - main_count * TLG6_W_BLOCK_SIZE as u32;
188    let mut bit_pool = vec![0u8; max_bit_length as usize / 8 + 5];
189    let mut pixelbuf = vec![0u32; width as usize * TLG6_H_BLOCK_SIZE + 1];
190    let mut filter_types = vec![0u8; x_block_count as usize * y_block_count as usize];
191    let mut lzss_text = [0u8; 4096];
192    let zero = if colors == 3 { 0xff_00_00_00u32 } else { 0 };
193    let zeroline = vec![zero; width as usize];
194    {
195        let mut p = 0;
196        let mut i = 0;
197        while i < 0x20u8 {
198            let mut j = 0;
199            while j < 0x10u8 {
200                lzss_text[p] = i;
201                p += 1;
202                lzss_text[p] = i;
203                p += 1;
204                lzss_text[p] = i;
205                p += 1;
206                lzss_text[p] = i;
207                p += 1;
208                lzss_text[p] = j;
209                p += 1;
210                lzss_text[p] = j;
211                p += 1;
212                lzss_text[p] = j;
213                p += 1;
214                lzss_text[p] = j;
215                p += 1;
216                j += 1;
217            }
218            i += 1;
219        }
220    }
221    {
222        let inbuf_size = src.read_u32()? as usize;
223        let mut inbuf = vec![0u8; inbuf_size];
224        src.read_exact(&mut inbuf)?;
225        tlg5_decompress_slide(&mut filter_types, &inbuf, inbuf_size, &mut lzss_text, 0);
226    }
227    let mut prevline = zeroline;
228    let mut outbuf = vec![0u32; width as usize * height as usize];
229    for y in (0..height).step_by(TLG6_H_BLOCK_SIZE) {
230        let y_lim = (y + TLG6_H_BLOCK_SIZE as u32).min(height);
231        let pixel_count = (y_lim - y) as usize * width as usize;
232        for c in 0..colors {
233            let mut bit_length = src.read_u32()?;
234            let method = (bit_length >> 30) & 3;
235            bit_length &= 0x3fff_ffff;
236            let byte_length = (bit_length + 7) / 8;
237            if byte_length as usize >= bit_pool.len() {
238                return Err(TlgError::Str(
239                    "Bit pool is too small for the given bit length".to_string(),
240                ));
241            }
242            src.read_exact(&mut bit_pool[..byte_length as usize])?;
243            match method {
244                0 => {
245                    tlg6_decode_golomb_values(
246                        &mut pixelbuf,
247                        pixel_count,
248                        &bit_pool,
249                        c == 0 && colors != 1,
250                        c,
251                    )?;
252                }
253                _ => return Err(TlgError::UnsupportedCompressedMethod(method as u8)),
254            }
255        }
256        let ft = &filter_types[(y as usize / TLG6_H_BLOCK_SIZE) * x_block_count as usize..];
257        let skip_bytes = (y_lim - y) as usize * TLG6_W_BLOCK_SIZE;
258        for yy in y..y_lim {
259            let curline =
260                &mut outbuf[(yy as usize * width as usize)..(yy as usize + 1) * width as usize];
261            let dir = (yy & 1) ^ 1 != 0;
262            let oddskip = ((y_lim - yy - 1) as isize) - (yy - y) as isize;
263            if main_count != 0 {
264                let start = TLG6_W_BLOCK_SIZE.min(width as usize) * (yy - y) as usize;
265                tlg6_decode_line(
266                    &prevline,
267                    curline,
268                    width,
269                    0,
270                    main_count as usize,
271                    ft,
272                    skip_bytes,
273                    &pixelbuf,
274                    start,
275                    zero,
276                    oddskip,
277                    dir,
278                )?;
279            }
280            if main_count != x_block_count {
281                let ww = TLG6_W_BLOCK_SIZE.min(fraction as usize);
282                let start = ww * (yy - y) as usize;
283                tlg6_decode_line(
284                    &prevline,
285                    curline,
286                    width,
287                    main_count as usize,
288                    x_block_count as usize,
289                    ft,
290                    skip_bytes,
291                    &pixelbuf,
292                    start,
293                    zero,
294                    oddskip,
295                    dir,
296                )?;
297            }
298            prevline = curline.to_vec();
299        }
300    }
301    let mut data = Vec::with_capacity(outbuf.len() * colors as usize);
302    for p in outbuf {
303        let t = p.to_le_bytes();
304        let b = t[0];
305        let g = t[1];
306        let r = t[2];
307        let a = t[3];
308        match color_type {
309            TlgColorType::Bgr24 => {
310                data.extend_from_slice(&[b, g, r]);
311            }
312            TlgColorType::Bgra32 => {
313                data.extend_from_slice(&[b, g, r, a]);
314            }
315            TlgColorType::Grayscale8 => {
316                data.extend_from_slice(&[b]);
317            }
318        };
319    }
320    Ok(Tlg {
321        tags: Default::default(),
322        version: 6,
323        width,
324        height,
325        color: color_type,
326        data,
327    })
328}
329
330fn internal_load_tlg<T: Read + Seek>(src: &mut T) -> Result<Tlg> {
331    let offset = src.stream_position()?;
332    let mut mark = [0; 11];
333    src.read_exact(&mut mark)?;
334    if &mark == b"TLG5.0\x00raw\x1a" {
335        load_tlg5(src)
336    } else if &mark == b"TLG6.0\x00raw\x1a" {
337        load_tlg6(src)
338    } else if &mark == b"XXXYYY\x00raw\x1a" {
339        let mut stream = XorStream::new(src);
340        stream.set_key(offset + 0xC, 0xAB);
341        stream.set_key(offset + 0x10, 0xAC);
342        load_tlg5(&mut stream)
343    } else if &mark == b"XXXZZZ\x00raw\x1a" {
344        let mut stream = XorStream::new(src);
345        stream.set_key(offset + 0xF, 0xAB);
346        stream.set_key(offset + 0x13, 0xAC);
347        load_tlg6(&mut stream)
348    } else if &mark == b"JKMXE8\x00raw\x1a" {
349        let mut stream = XorStream::new(src);
350        stream.set_key(offset + 0xC, 0x1A);
351        stream.set_key(offset + 0x10, 0x1C);
352        load_tlg5(&mut stream)
353    } else {
354        Err(TlgError::InvalidFormat)
355    }
356}
357
358/// Decode TLG image
359pub fn load_tlg<T: Read + Seek>(mut src: T) -> Result<Tlg> {
360    src.rewind()?;
361    let mut mark = [0; 11];
362    src.read_exact(&mut mark)?;
363    if &mark == b"TLG0.0\x00sds\x1a" {
364        let rawlen = src.read_u32()?;
365        let mut tlg = internal_load_tlg(&mut src)?;
366        let newlen = rawlen as u64 + 15;
367        src.seek(SeekFrom::Start(newlen))?;
368        let mut check = true;
369        while check {
370            let mut chunkname = [0; 4];
371            if src.read(&mut chunkname)? != 4 {
372                break;
373            }
374            let chunksize = src.read_u32()?;
375            if &chunkname == b"tags" {
376                let mut tag = vec![0; chunksize as usize];
377                src.read_exact(&mut tag)?;
378                let mut i = 0;
379                let len = tag.len();
380                while i < len {
381                    let mut namelen = 0usize;
382                    let mut c = tag[i];
383                    let mut ok = true;
384                    while c >= b'0' && c <= b'9' {
385                        namelen = namelen * 10 + (c - b'0') as usize;
386                        i += 1;
387                        if i >= len {
388                            ok = false;
389                            break;
390                        }
391                        c = tag[i];
392                    }
393                    if !ok {
394                        break;
395                    }
396                    if c != b':' {
397                        check = false;
398                        break;
399                    }
400                    i += 1;
401                    let name = tag[i..i + namelen].to_vec();
402                    i += namelen;
403                    if i >= len {
404                        break;
405                    }
406                    c = tag[i];
407                    if c != b'=' {
408                        check = false;
409                        break;
410                    }
411                    i += 1;
412                    let mut valuelen = 0usize;
413                    c = tag[i];
414                    ok = true;
415                    while c >= b'0' && c <= b'9' {
416                        valuelen = valuelen * 10 + (c - b'0') as usize;
417                        i += 1;
418                        if i >= len {
419                            ok = false;
420                            break;
421                        }
422                        c = tag[i];
423                    }
424                    if !ok {
425                        break;
426                    }
427                    if c != b':' {
428                        check = false;
429                        break;
430                    }
431                    i += 1;
432                    let value = tag[i..i + valuelen].to_vec();
433                    i += valuelen;
434                    if i >= len {
435                        check = false;
436                        break;
437                    }
438                    c = tag[i];
439                    if c != b',' {
440                        check = false;
441                        break;
442                    }
443                    i += 1;
444                    tlg.tags.insert(name, value);
445                }
446            } else {
447                // skip the chunk
448                src.seek_relative(chunksize as i64)?;
449            }
450        }
451        Ok(tlg)
452    } else {
453        src.rewind()?;
454        internal_load_tlg(&mut src)
455    }
456}