use crate::{
SymbolType, color::Color, decoder::Modifier, decoder::decode_e, img_scanner::ImageScanner,
};
const NUM_CHARS: usize = 108;
const FNC3: u8 = 0x60;
const FNC2: u8 = 0x61;
const SHIFT: u8 = 0x62;
const CODE_C: u8 = 0x63;
const CODE_A: u8 = 0x65;
const FNC1: u8 = 0x66;
const START_A: u8 = 0x67;
const START_C: u8 = 0x69;
const STOP_FWD: u8 = 0x6a;
const STOP_REV: u8 = 0x6b;
macro_rules! zassert {
($condition:expr, $retval:expr, $($arg:tt)*) => {
if !$condition {
return $retval;
}
};
}
static CHARACTERS: [u8; NUM_CHARS] = [
0x5c, 0xbf, 0xa1, 0x2a, 0xc5, 0x0c, 0xa4, 0x2d, 0xe3, 0x0f, 0x5f, 0xe4, 0x6b, 0xe8, 0x69, 0xa7, 0xe7, 0xc1, 0x51, 0x1e, 0x83, 0xd9, 0x00, 0x84, 0x1f, 0xc7, 0x0d, 0x33, 0x86, 0xb5, 0x0e, 0x15, 0x87, 0x10, 0xda, 0x11, 0x36, 0xe5, 0x18, 0x37, 0xcc, 0x13, 0x39, 0x89, 0x97, 0x14, 0x1b, 0x8a, 0x3a, 0xbd, 0xa2, 0x5e, 0x01, 0x85, 0xb0, 0x02, 0xa3, 0xa5, 0x2c, 0x16, 0x88, 0xbc, 0x12, 0xa6, 0x61, 0xe6, 0x56, 0x62, 0x19, 0xdb, 0x1a, 0xa8, 0x32, 0x1c, 0x8b, 0xcd, 0x1d, 0xa9, 0xc3, 0x20, 0xc4, 0x50, 0x5d, 0xc0, 0x2b, 0xc6, 0x2e, 0x53, 0x60, 0x31, 0x52, 0xc2, 0x34, 0xc8, 0x55, 0x57, 0x3e, 0xce, 0x3b, 0xc9, 0x6a, 0x54, 0x4f, 0x38, 0x58, 0xcb, 0x2f, 0xca, ];
static LO_BASE: [u8; 8] = [0x00, 0x07, 0x0c, 0x19, 0x24, 0x32, 0x40, 0x47];
static LO_OFFSET: [u8; 0x80] = [
0xff, 0xf0, 0xff, 0x1f, 0xff, 0xf2, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xf4, 0xf5, 0xff, 0x6f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf1, 0xff, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x4f, 0xff, 0x0f, 0xf1, 0xf2, 0xff, 0x3f, 0xff, 0xf4, 0xf5, 0xf6, 0xf7, 0x89, 0xff, 0xab, 0xff, 0xfc, 0xff, 0xff, 0x0f, 0x1f, 0x23, 0x45, 0xf6, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xf9, 0xaf, 0xf0, 0xf1, 0xff, 0x2f, 0xff, 0xf3, 0xff, 0xff, 0x4f, 0x5f, 0x67, 0x89, 0xfa, 0xbf, 0xff, 0xcd, 0xf0, 0xf1, 0xf2, 0x3f, 0xf4, 0x56, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x8f, 0x9a, 0xff, 0xbc, 0xdf, 0x0f, 0x1f, 0xf2, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xf4, 0xff, 0xf5, 0x6f, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x1f, 0x23, 0xff, 0x45, 0x6f, 0xff, 0xff, 0xf7, 0xff, 0xf8, 0x9f, 0xff, 0xff, 0xff, 0xff, ];
fn decode_lo(sig: i32) -> i8 {
let offset =
(((sig >> 1) & 0x01) | ((sig >> 3) & 0x06) | ((sig >> 5) & 0x18) | ((sig >> 7) & 0x60))
as u8;
let mut idx = LO_OFFSET[offset as usize];
if (sig & 1) != 0 {
idx &= 0xf;
} else {
idx >>= 4;
}
if idx == 0xf {
return -1;
}
let base = ((sig >> 11) | ((sig >> 9) & 1)) as u8;
zassert!(
base < 8,
-1,
"sig={:x} offset={:x} idx={:x} base={:x}\n",
sig,
offset,
idx,
base
);
let idx = idx + LO_BASE[base as usize];
zassert!(
idx <= 0x50,
-1,
"sig={:x} offset={:x} base={:x} idx={:x}\n",
sig,
offset,
base,
idx
);
CHARACTERS[idx as usize] as i8
}
fn decode_hi(mut sig: i32) -> i8 {
let mut rev = (sig & 0x4400) != 0;
if rev {
sig = ((sig >> 12) & 0x000f)
| ((sig >> 4) & 0x00f0)
| ((sig << 4) & 0x0f00)
| ((sig << 12) & 0xf000);
}
let mut idx = match sig {
0x0014 => 0x0,
0x0025 => 0x1,
0x0034 => 0x2,
0x0134 => 0x3,
0x0143 => 0x4,
0x0243 => 0x5,
0x0341 => 0x6,
0x0352 => 0x7,
0x1024 => 0x8,
0x1114 => 0x9,
0x1134 => 0xa,
0x1242 => 0xb,
0x1243 => 0xc,
0x1441 => {
rev = false;
0xd
}
_ => return -1,
};
if rev {
idx += 0xe;
}
CHARACTERS[0x51 + idx] as i8
}
fn calc_check(c: u8) -> u8 {
if (c & 0x80) == 0 {
return 0x18;
}
let c = c & 0x7f;
if c < 0x3d {
return if c < 0x30 && c != 0x17 { 0x10 } else { 0x20 };
}
if c < 0x50 {
return if c == 0x4d { 0x20 } else { 0x10 };
}
if c < 0x67 { 0x20 } else { 0x10 }
}
fn decode6(dcode: &ImageScanner) -> i8 {
let s = dcode.code128.s6;
if s < 5 {
return -1;
}
let sig = if dcode.color() == Color::Bar {
(decode_e(dcode.get_width(0) + dcode.get_width(1), s, 11) << 12)
| (decode_e(dcode.get_width(1) + dcode.get_width(2), s, 11) << 8)
| (decode_e(dcode.get_width(2) + dcode.get_width(3), s, 11) << 4)
| decode_e(dcode.get_width(3) + dcode.get_width(4), s, 11)
} else {
(decode_e(dcode.get_width(5) + dcode.get_width(4), s, 11) << 12)
| (decode_e(dcode.get_width(4) + dcode.get_width(3), s, 11) << 8)
| (decode_e(dcode.get_width(3) + dcode.get_width(2), s, 11) << 4)
| decode_e(dcode.get_width(2) + dcode.get_width(1), s, 11)
};
if sig < 0 {
return -1;
}
let c = if (sig & 0x4444) != 0 {
decode_hi(sig)
} else {
decode_lo(sig)
};
if c == -1 {
return -1;
}
let bars = if dcode.color() == Color::Bar {
dcode.get_width(0) + dcode.get_width(2) + dcode.get_width(4)
} else {
dcode.get_width(1) + dcode.get_width(3) + dcode.get_width(5)
};
let bars = bars * 11 * 4 / s;
let chk = calc_check(c as u8);
if (chk as i32 - 7) > bars as i32 || bars as i32 > (chk as i32 + 7) {
return -1;
}
c & 0x7f
}
fn validate_checksum(dcode: &ImageScanner) -> bool {
if dcode.code128.character() < 3 {
return true;
}
let buf = dcode.buffer_slice();
let idx = if dcode.code128.direction() != 0 {
(dcode.code128.character() - 1) as usize
} else {
0
};
let mut sum = buf[idx] as u32;
if sum >= 103 {
sum -= 103;
}
let mut acc: u32 = 0;
for i in (1..(dcode.code128.character() - 2) as usize).rev() {
zassert!(
sum < 103,
true,
"dir={:x} i={:x} sum={:x} acc={:x}\n",
dcode.code128.direction(),
i,
sum,
acc
);
let idx = if dcode.code128.direction() != 0 {
(dcode.code128.character() as usize) - 1 - i
} else {
i
};
acc += buf[idx] as u32;
if acc >= 103 {
acc -= 103;
}
zassert!(
acc < 103,
true,
"dir={:x} i={:x} sum={:x} acc={:x}\n",
dcode.code128.direction(),
i,
sum,
acc
);
sum += acc;
if sum >= 103 {
sum -= 103;
}
}
let idx = if dcode.code128.direction() != 0 {
1
} else {
(dcode.code128.character() - 2) as usize
};
let check = buf[idx] as u32;
sum != check
}
fn postprocess_c(dcode: &mut ImageScanner, start: usize, end: usize, dst: usize) -> u32 {
let delta = end - start;
let old_len = dcode.code128.character() as usize;
let newlen = old_len + delta;
if dcode.set_buffer_capacity(newlen).is_err() {
return SymbolType::None as u32;
}
let buf = match dcode.buffer_mut_slice(newlen) {
Ok(buf) => buf,
Err(_) => return SymbolType::None as u32,
};
buf.copy_within(start..old_len, start + delta);
for i in 0..delta {
let j = dst + i * 2;
let mut code = buf[start + delta + i];
buf[j] = b'0';
if code >= 50 {
code -= 50;
buf[j] += 5;
}
if code >= 30 {
code -= 30;
buf[j] += 3;
}
if code >= 20 {
code -= 20;
buf[j] += 2;
}
if code >= 10 {
code -= 10;
buf[j] += 1;
}
zassert!(
buf[j] <= b'9',
delta as u32,
"start={:x} end={:x} i={:x} j={:x}\n",
start,
end,
i,
j
);
zassert!(
code <= 9,
delta as u32,
"start={:x} end={:x} i={:x} j={:x}\n",
start,
end,
i,
j
);
buf[j + 1] = b'0' + code;
}
dcode.code128.set_character(newlen as i16);
delta as u32
}
fn postprocess(dcode: &mut ImageScanner) -> bool {
dcode.modifiers = 0;
dcode.direction = 1 - 2 * (dcode.code128.direction() as i32);
let character_count = dcode.code128.character() as usize;
let direction = dcode.code128.direction();
{
let buf = match dcode.buffer_mut_slice(character_count) {
Ok(buf) => buf,
Err(_) => return true,
};
if direction != 0 {
let half = character_count / 2;
for i in 0..half {
let j = character_count - 1 - i;
buf.swap(i, j);
}
zassert!(
buf[character_count - 1] == STOP_REV,
true,
"dir={:x}\n",
direction
);
} else {
zassert!(
buf[character_count - 1] == STOP_FWD,
true,
"dir={:x}\n",
direction
);
}
let code = buf[0];
zassert!(
(START_A..=START_C).contains(&code),
true,
"code={:x}\n",
code
);
}
let start_code = {
let buf = match dcode.buffer_mut_slice(character_count) {
Ok(buf) => buf,
Err(_) => return true,
};
buf[0]
};
let mut charset = start_code - START_A;
let mut cexp = if start_code == START_C { 1 } else { 0 };
let mut i = 1usize;
let mut j = 0usize;
while i < (character_count - 2) {
let code = {
let buf = match dcode.buffer_mut_slice(character_count.max(j + 1)) {
Ok(buf) => buf,
Err(_) => return true,
};
buf[i]
};
zassert!(
(code & 0x80) == 0,
true,
"i={:x} j={:x} code={:02x} charset={:x} cexp={:x}\n",
i,
j,
code,
charset,
cexp
);
if (charset & 0x2) != 0 && code < 100 {
i += 1;
continue;
} else if code < 0x60 {
let mut ascii_code = code + 0x20;
if (charset == 0 || charset == 0x81) && ascii_code >= 0x60 {
ascii_code -= 0x60;
}
let buf = match dcode.buffer_mut_slice(character_count.max(j + 1)) {
Ok(buf) => buf,
Err(_) => return true,
};
buf[j] = ascii_code;
j += 1;
if (charset & 0x80) != 0 {
charset &= 0x7f;
}
} else {
if (charset & 0x2) != 0 {
zassert!(
cexp != 0,
true,
"i={:x} j={:x} code={:02x} charset={:x} cexp={:x}\n",
i,
j,
code,
charset,
cexp
);
let delta = postprocess_c(dcode, cexp, i, j);
i += delta as usize;
j += (delta * 2) as usize;
cexp = 0;
}
if code < CODE_C {
if code == SHIFT {
charset |= 0x80;
} else if code == FNC2 {
} else if code == FNC3 {
}
} else if code == FNC1 {
if i == 1 {
dcode.modifiers |= Modifier::Gs1.bit();
} else if i == 2 {
dcode.modifiers |= Modifier::Aim.bit();
} else if i < (character_count - 3) {
let buf = match dcode.buffer_mut_slice(j + 1) {
Ok(buf) => buf,
Err(_) => return true,
};
buf[j] = 0x1d;
j += 1;
}
} else if code >= START_A {
return true;
} else {
let newset = CODE_A - code;
zassert!(
(CODE_C..=CODE_A).contains(&code),
true,
"i={:x} j={:x} code={:02x} charset={:x} cexp={:x}\n",
i,
j,
code,
charset,
cexp
);
if newset != charset {
charset = newset;
} else {
}
}
if (charset & 0x2) != 0 {
cexp = i + 1;
}
}
i += 1;
}
if (charset & 0x2) != 0 {
zassert!(
cexp != 0,
true,
"i={:x} j={:x} code={:02x} charset={:x} cexp={:x}\n",
i,
j,
0u8, charset,
cexp
);
let delta = postprocess_c(dcode, cexp, i, j);
j += (delta * 2) as usize;
}
zassert!(j < dcode.buffer_capacity(), true, "j={:02x}\n", j);
dcode.set_buffer_len(j);
let buf = match dcode.buffer_mut_slice(j + 1) {
Ok(buf) => buf,
Err(_) => return true,
};
buf[j] = 0;
dcode.code128.set_character(j as i16);
false
}
pub(crate) fn decode_code128(dcode: &mut ImageScanner) -> SymbolType {
dcode.code128.s6 = dcode
.code128
.s6
.wrapping_sub(dcode.get_width(6))
.wrapping_add(dcode.get_width(0));
if (dcode.code128.character() < 0 && dcode.color() != Color::Space)
|| (dcode.code128.character() >= 0
&& (dcode.code128.element() + 1 != 6
|| dcode.color() as u8 != dcode.code128.direction()))
{
if dcode.code128.character() >= 0 {
dcode.code128.set_element(dcode.code128.element() + 1);
}
return SymbolType::None;
}
dcode.code128.set_element(0);
let c = decode6(dcode);
if dcode.code128.character() < 0 {
let qz = dcode.get_width(6);
if c < START_A as i8 || c > STOP_REV as i8 || c == STOP_FWD as i8 {
return SymbolType::None;
}
if qz != 0 && qz < (dcode.code128.s6 * 3) / 4 {
return SymbolType::None;
}
dcode.code128.set_character(1);
if c == STOP_REV as i8 {
dcode.code128.set_direction(Color::Bar as u8);
dcode.code128.set_element(7);
} else {
dcode.code128.set_direction(Color::Space as u8);
}
dcode.code128.set_start(c as u8);
dcode.code128.width = dcode.code128.s6;
return SymbolType::None;
} else if c < 0
|| dcode
.set_buffer_capacity(dcode.code128.character() as usize + 1)
.is_err()
{
if dcode.code128.character() > 1 {
dcode.release_lock(SymbolType::Code128);
}
dcode.code128.set_character(-1);
return SymbolType::None;
} else {
let dw = dcode.code128.width.abs_diff(dcode.code128.s6);
let dw = dw * 4;
if dw > dcode.code128.width {
if dcode.code128.character() > 1 {
dcode.release_lock(SymbolType::Code128);
}
dcode.code128.set_character(-1);
return SymbolType::None;
}
}
dcode.code128.width = dcode.code128.s6;
let capacity = dcode.buffer_capacity();
zassert!(
(capacity as i16) > dcode.code128.character(),
SymbolType::None,
"alloc={:x} idx={:x} c={:02x}\n",
capacity,
dcode.code128.character(),
c
);
if dcode.code128.character() == 1 {
if !dcode.acquire_lock(SymbolType::Code128) {
dcode.code128.set_character(-1);
return SymbolType::None;
}
let start = dcode.code128.start();
if let Ok(buf) = dcode.buffer_mut_slice(1) {
buf[0] = start;
}
}
let character = dcode.code128.character();
if let Ok(buf) = dcode.buffer_mut_slice((character + 1) as usize) {
buf[character as usize] = c as u8;
}
dcode.code128.set_character(character + 1);
if dcode.code128.character() > 2
&& ((dcode.code128.direction() != 0 && c >= START_A as i8 && c <= START_C as i8)
|| (dcode.code128.direction() == 0 && c == STOP_FWD as i8))
{
let mut sym = SymbolType::Code128;
#[allow(clippy::if_same_then_else)]
let (min_len, max_len) = dcode
.get_length_limits(SymbolType::Code128)
.unwrap_or((4, 0));
if validate_checksum(dcode)
|| postprocess(dcode)
|| dcode.code128.character() < min_len as i16
|| (max_len > 0 && dcode.code128.character() > max_len as i16)
{
sym = SymbolType::None;
}
dcode.code128.set_character(-1);
if sym == SymbolType::None {
dcode.release_lock(SymbolType::Code128);
}
return sym;
}
SymbolType::None
}