1use crate::codec::Dims;
15use crate::error::{Error, Result};
16use std::io::Read;
17
18const HEADER: usize = 24;
19const TS: u32 = 16;
20const MAX_PIXELS: u64 = 1 << 26;
21const MAX_PAYLOAD: u32 = (1 << 31) - 1;
22
23pub struct RefDecodeResult {
25 pub dims: Dims,
27 pub pixels: Vec<u8>,
29}
30
31fn div_ceil(a: u32, b: u32) -> u32 {
33 a.div_ceil(b)
34}
35
36fn push_target(t: u8, w: u32, h: u32, sx: u32, sy: u32) -> (u32, u32, u32, u32) {
40 match t {
41 0 => (w, h, sx, sy),
42 1 => (w, h, w - 1 - sx, sy),
43 2 => (w, h, sx, h - 1 - sy),
44 3 => (w, h, w - 1 - sx, h - 1 - sy),
45 4 => (h, w, h - 1 - sy, sx),
46 5 => {
47 let (ow, oh, ox, oy) = push_target(4, w, h, sx, sy);
48 push_target(1, ow, oh, ox, oy)
49 }
50 6 => {
51 let (ow, oh, ox, oy) = push_target(4, w, h, sx, sy);
52 push_target(3, ow, oh, ox, oy)
53 }
54 7 => {
55 let (ow, oh, ox, oy) = push_target(4, w, h, sx, sy);
56 let (w2, h2, ox2, oy2) = push_target(3, ow, oh, ox, oy);
57 push_target(1, w2, h2, ox2, oy2)
58 }
59 _ => unreachable!("range validated by caller"),
60 }
61}
62
63fn push_transform(t: u8, w: u32, h: u32, data: &[u8]) -> Vec<u8> {
64 let (ow, oh) = if t >= 4 { (h, w) } else { (w, h) };
65 let mut out = vec![0u8; (ow * oh * 3) as usize];
66 let mut s = 0usize;
67 for sy in 0..h {
68 for sx in 0..w {
69 let (_ow, _oh, ox, oy) = push_target(t, w, h, sx, sy);
70 let ow_ = if t >= 4 { h } else { w };
71 let d = ((oy * ow_ + ox) * 3) as usize;
72 out[d] = data[s];
73 out[d + 1] = data[s + 1];
74 out[d + 2] = data[s + 2];
75 s += 3;
76 }
77 }
78 out
79}
80
81pub fn ref_decode(buf: &[u8]) -> Result<RefDecodeResult> {
83 if buf.len() < HEADER {
84 return Err(Error::fmt_msg("short"));
85 }
86 if buf[0..4] != *b"MNF5" {
87 return Err(Error::fmt_msg("magic"));
88 }
89 let u32le = |o: usize| u32::from_le_bytes(buf[o..o + 4].try_into().unwrap());
90 let width = u32le(4);
91 let height = u32le(8);
92 if width == 0 || height == 0 {
93 return Err(Error::fmt_msg("dims"));
94 }
95 if width as u64 * height as u64 > MAX_PIXELS {
96 return Err(Error::fmt_msg("pixels"));
97 }
98 let ts = u32le(12);
99 if ts != TS {
100 return Err(Error::fmt_msg("tilesize"));
101 }
102 let cols = div_ceil(width, TS);
103 let rows = div_ceil(height, TS);
104 let tile_count = u32le(16);
105 if tile_count != cols * rows {
106 return Err(Error::fmt_msg("tilecount"));
107 }
108 let payload_len = u32le(20);
109 if payload_len > MAX_PAYLOAD {
110 return Err(Error::fmt_msg("payloadlen"));
111 }
112 if buf.len() != HEADER + payload_len as usize {
113 return Err(Error::fmt_msg("filelen"));
114 }
115 let zlib = &buf[HEADER..];
116
117 let limit = tile_count as u64 * (1 + 5 + (TS * TS * 3) as u64);
118 let mut raw: Vec<u8> = Vec::new();
119 {
120 let mut d = flate2::read::ZlibDecoder::new(zlib).take(limit + 1);
121 d.read_to_end(&mut raw)
122 .map_err(|_| Error::Decompress("corrupt zlib stream"))?;
123 }
124 if raw.len() as u64 > limit {
125 return Err(Error::fmt_msg("bomb"));
126 }
127
128 let mut pixels = vec![0u8; width as usize * height as usize * 3];
129 let mut decoded: Vec<(u32, u32, Vec<u8>)> = Vec::with_capacity(tile_count as usize);
130 let mut ip = 0usize;
131 for i in 0..tile_count {
132 let row = i / cols;
133 let col = i % cols;
134 let tw = TS.min(width - col * TS);
135 let th = TS.min(height - row * TS);
136 let n = (tw * th * 3) as usize;
137
138 if ip >= raw.len() {
139 return Err(Error::fmt_msg("eof"));
140 }
141 let mode = raw[ip];
142 ip += 1;
143
144 let refs: Option<(usize, u8)> = match mode {
145 0 => None,
146 1 => {
147 if ip + 4 > raw.len() {
148 return Err(Error::fmt_msg("refmeta"));
149 }
150 let idx = u32::from_le_bytes(raw[ip..ip + 4].try_into().unwrap()) as usize;
151 ip += 4;
152 Some((idx, 0))
153 }
154 2 => {
155 if ip + 5 > raw.len() {
156 return Err(Error::fmt_msg("refmeta"));
157 }
158 let idx = u32::from_le_bytes(raw[ip..ip + 4].try_into().unwrap()) as usize;
159 let tr = raw[ip + 4];
160 ip += 5;
161 Some((idx, tr))
162 }
163 _ => return Err(Error::fmt_msg("mode")),
164 };
165
166 if ip + n > raw.len() {
167 return Err(Error::fmt_msg("body"));
168 }
169 let body = raw[ip..ip + n].to_vec();
170 ip += n;
171
172 let tile = match refs {
173 None => body,
174 Some((src, tr)) => {
175 if src >= i as usize {
176 return Err(Error::fmt_msg("reforder"));
177 }
178 if tr >= 8 {
179 return Err(Error::fmt_msg("tranform-range"));
180 }
181 let (rw, rh, ref_rgb) = &decoded[src];
182 if *rw != tw || *rh != th {
183 return Err(Error::fmt_msg("refshape"));
184 }
185 if tw != th && tr >= 4 {
186 return Err(Error::fmt_msg("xform-shape"));
187 }
188 let base = if tr == 0 {
189 ref_rgb.clone()
190 } else {
191 push_transform(tr, tw, th, ref_rgb)
192 };
193 base.iter()
194 .zip(body.iter())
195 .map(|(a, r)| a.wrapping_add(*r))
196 .collect()
197 }
198 };
199 decoded.push((tw, th, tile.clone()));
200
201 for y in 0..th as usize {
202 let py = row as usize * TS as usize + y;
203 for x in 0..tw as usize {
204 let pxx = col as usize * TS as usize + x;
205 let d = (py * width as usize + pxx) * 3;
206 let s = (y * tw as usize + x) * 3;
207 pixels[d] = tile[s];
208 pixels[d + 1] = tile[s + 1];
209 pixels[d + 2] = tile[s + 2];
210 }
211 }
212 }
213 if ip != raw.len() {
214 return Err(Error::fmt_msg("extra"));
215 }
216 Ok(RefDecodeResult {
217 dims: Dims { width, height },
218 pixels,
219 })
220}