6f1a1a274e
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
954 lines
19 KiB
C
954 lines
19 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <iconv.h>
|
|
#if HAVE_CONFIG_H
|
|
#include "../config.h"
|
|
#endif
|
|
#include "../qrspec.h"
|
|
#include "../bitstream.h"
|
|
#include "../mask.h"
|
|
#include "../mqrspec.h"
|
|
#include "../mmask.h"
|
|
#include "common.h"
|
|
#include "decoder.h"
|
|
|
|
static unsigned int bitToInt(unsigned char *bits, int length)
|
|
{
|
|
int i;
|
|
unsigned int val = 0;
|
|
|
|
for(i=0; i<length; i++) {
|
|
val = val << 1;
|
|
val |= (bits[i] & 1);
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
static int decodeLength(int *bits_length, unsigned char **bits, QRencodeMode mode, int version, int mqr)
|
|
{
|
|
int i;
|
|
int length = 0;
|
|
int lbits;
|
|
|
|
if(mqr) {
|
|
lbits = MQRspec_lengthIndicator(mode, version);
|
|
} else {
|
|
lbits = QRspec_lengthIndicator(mode, version);
|
|
}
|
|
|
|
if(*bits_length < lbits) {
|
|
printf("Bit length is too short: %d\n", *bits_length);
|
|
return 0;
|
|
}
|
|
|
|
length = 0;
|
|
for(i=0; i<lbits; i++) {
|
|
length = length << 1;
|
|
length += (*bits)[i];
|
|
}
|
|
|
|
*bits_length -= lbits;
|
|
*bits += lbits;
|
|
|
|
return length;
|
|
}
|
|
|
|
static DataChunk *decodeNum(int *bits_length, unsigned char **bits, int version, int mqr)
|
|
{
|
|
int i;
|
|
int size, sizeInBit, words, remain;
|
|
unsigned char *p;
|
|
char *buf, *q;
|
|
unsigned int val;
|
|
DataChunk *chunk;
|
|
|
|
size = decodeLength(bits_length, bits, QR_MODE_NUM, version, mqr);
|
|
if(size < 0) return NULL;
|
|
|
|
words = size / 3;
|
|
remain = size - words * 3;
|
|
sizeInBit = words * 10;
|
|
if(remain == 2) {
|
|
sizeInBit += 7;
|
|
} else if(remain == 1) {
|
|
sizeInBit += 4;
|
|
}
|
|
if(*bits_length < sizeInBit) {
|
|
printf("Bit length is too short: %d, expected %d.\n", *bits_length, sizeInBit);
|
|
return NULL;
|
|
}
|
|
|
|
buf = (char *)malloc((size_t)size + 1);
|
|
p = *bits;
|
|
q = buf;
|
|
for(i=0; i<words; i++) {
|
|
val = bitToInt(p, 10);
|
|
sprintf(q, "%03d", val);
|
|
p += 10;
|
|
q += 3;
|
|
}
|
|
if(remain == 2) {
|
|
val = bitToInt(p, 7);
|
|
sprintf(q, "%02d", val);
|
|
} else if(remain == 1) {
|
|
val = bitToInt(p, 4);
|
|
sprintf(q, "%1d", val);
|
|
}
|
|
buf[size] = '\0';
|
|
|
|
chunk = DataChunk_new(QR_MODE_NUM);
|
|
chunk->size = size;
|
|
chunk->data = (unsigned char *)buf;
|
|
*bits_length -= sizeInBit;
|
|
*bits += sizeInBit;
|
|
|
|
return chunk;
|
|
}
|
|
|
|
static const char decodeAnTable[45] = {
|
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
|
|
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
|
|
'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*',
|
|
'+', '-', '.', '/', ':'
|
|
};
|
|
|
|
static DataChunk *decodeAn(int *bits_length, unsigned char **bits, int version, int mqr)
|
|
{
|
|
int i;
|
|
int size, sizeInBit, words, remain;
|
|
unsigned char *p;
|
|
char *buf, *q;
|
|
unsigned int val;
|
|
int ch, cl;
|
|
DataChunk *chunk;
|
|
|
|
size = decodeLength(bits_length, bits, QR_MODE_AN, version, mqr);
|
|
if(size < 0) return NULL;
|
|
|
|
words = size / 2;
|
|
remain = size - words * 2;
|
|
sizeInBit = words * 11 + remain * 6;
|
|
if(*bits_length < sizeInBit) {
|
|
printf("Bit length is too short: %d, expected %d.\n", *bits_length, sizeInBit);
|
|
return NULL;
|
|
}
|
|
|
|
buf = (char *)malloc((size_t)size + 1);
|
|
p = *bits;
|
|
q = buf;
|
|
for(i=0; i<words; i++) {
|
|
val = bitToInt(p, 11);
|
|
ch = (int)(val / 45);
|
|
cl = (int)(val % 45);
|
|
sprintf(q, "%c%c", decodeAnTable[ch], decodeAnTable[cl]);
|
|
p += 11;
|
|
q += 2;
|
|
}
|
|
if(remain == 1) {
|
|
val = bitToInt(p, 6);
|
|
sprintf(q, "%c", decodeAnTable[val]);
|
|
}
|
|
|
|
chunk = DataChunk_new(QR_MODE_AN);
|
|
chunk->size = size;
|
|
chunk->data = (unsigned char *)buf;
|
|
*bits_length -= sizeInBit;
|
|
*bits += sizeInBit;
|
|
|
|
return chunk;
|
|
}
|
|
|
|
static DataChunk *decode8(int *bits_length, unsigned char **bits, int version, int mqr)
|
|
{
|
|
int i;
|
|
int size, sizeInBit;
|
|
unsigned char *p;
|
|
unsigned char *buf, *q;
|
|
DataChunk *chunk;
|
|
|
|
size = decodeLength(bits_length, bits, QR_MODE_8, version, mqr);
|
|
if(size < 0) return NULL;
|
|
|
|
sizeInBit = size * 8;
|
|
if(*bits_length < sizeInBit) {
|
|
printf("Bit length is too short: %d, expected %d.\n", *bits_length, sizeInBit);
|
|
return NULL;
|
|
}
|
|
|
|
buf = (unsigned char *)malloc((size_t)size);
|
|
p = *bits;
|
|
q = buf;
|
|
for(i=0; i<size; i++) {
|
|
*q = (unsigned char)bitToInt(p, 8);
|
|
p += 8;
|
|
q += 1;
|
|
}
|
|
|
|
chunk = DataChunk_new(QR_MODE_8);
|
|
chunk->size = size;
|
|
chunk->data = buf;
|
|
*bits_length -= sizeInBit;
|
|
*bits += sizeInBit;
|
|
|
|
return chunk;
|
|
}
|
|
|
|
static DataChunk *decodeKanji(int *bits_length, unsigned char **bits, int version, int mqr)
|
|
{
|
|
int i;
|
|
int size, sizeInBit;
|
|
unsigned char *p;
|
|
char *buf, *q;
|
|
unsigned int val;
|
|
unsigned int ch, cl;
|
|
DataChunk *chunk;
|
|
|
|
size = decodeLength(bits_length, bits, QR_MODE_KANJI, version, mqr);
|
|
if(size < 0) return NULL;
|
|
|
|
sizeInBit = size * 13;
|
|
if(*bits_length < sizeInBit) {
|
|
printf("Bit length is too short: %d, expected %d.\n", *bits_length, sizeInBit);
|
|
return NULL;
|
|
}
|
|
|
|
buf = (char *)malloc((size_t)size * 2 + 1);
|
|
p = *bits;
|
|
q = buf;
|
|
for(i=0; i<size; i++) {
|
|
val = bitToInt(p, 13);
|
|
ch = val / 0xc0;
|
|
cl = val - ch * 0xc0;
|
|
val = ch * 256 + cl;
|
|
if(val >= 0x1f00) {
|
|
val += 0xc140;
|
|
} else {
|
|
val += 0x8140;
|
|
}
|
|
sprintf(q, "%c%c", (val>>8) & 0xff, val & 0xff);
|
|
p += 13;
|
|
q += 2;
|
|
}
|
|
|
|
chunk = DataChunk_new(QR_MODE_KANJI);
|
|
chunk->size = size * 2;
|
|
chunk->data = (unsigned char *)buf;
|
|
*bits_length -= sizeInBit;
|
|
*bits += sizeInBit;
|
|
|
|
return chunk;
|
|
}
|
|
|
|
static DataChunk *decodeChunk(int *bits_length, unsigned char **bits, int version)
|
|
{
|
|
unsigned int val;
|
|
|
|
if(*bits_length < 4) {
|
|
return NULL;
|
|
}
|
|
val = bitToInt(*bits, 4);
|
|
*bits_length -= 4;
|
|
*bits += 4;
|
|
switch(val) {
|
|
case 0:
|
|
return NULL;
|
|
case 1:
|
|
return decodeNum(bits_length, bits, version, 0);
|
|
case 2:
|
|
return decodeAn(bits_length, bits, version, 0);
|
|
case 4:
|
|
return decode8(bits_length, bits, version, 0);
|
|
case 8:
|
|
return decodeKanji(bits_length, bits, version, 0);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
printf("Invalid mode in a chunk: %d\n", val);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static DataChunk *decodeChunkMQR(int *bits_length, unsigned char **bits, int version)
|
|
{
|
|
int modebits, termbits;
|
|
unsigned int val;
|
|
|
|
modebits = version - 1;
|
|
termbits = version * 2 + 1;
|
|
if(*bits_length >= termbits) {
|
|
val = bitToInt(*bits, termbits);
|
|
if(val == 0) {
|
|
*bits += termbits;
|
|
*bits_length -= termbits;
|
|
return NULL;
|
|
}
|
|
} else {
|
|
if(*bits_length < modebits) {
|
|
val = bitToInt(*bits, *bits_length);
|
|
} else {
|
|
val = bitToInt(*bits, modebits);
|
|
}
|
|
if(val == 0) {
|
|
return NULL;
|
|
} else {
|
|
printf("Terminating bits include 1-bit.\n");
|
|
return NULL;
|
|
}
|
|
}
|
|
val = bitToInt(*bits, modebits);
|
|
if(version == 4 && val > 3) {
|
|
printf("Invalid mode number %d.\n", val);
|
|
}
|
|
*bits_length -= modebits;
|
|
*bits += modebits;
|
|
switch(val) {
|
|
case 0:
|
|
return decodeNum(bits_length, bits, version, 1);
|
|
case 1:
|
|
return decodeAn(bits_length, bits, version, 1);
|
|
case 2:
|
|
return decode8(bits_length, bits, version, 1);
|
|
case 3:
|
|
return decodeKanji(bits_length, bits, version, 1);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
printf("Invalid mode in a chunk: %d\n", val);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int appendChunk(QRdata *qrdata, int *bits_length, unsigned char **bits)
|
|
{
|
|
DataChunk *chunk;
|
|
|
|
if(qrdata->mqr) {
|
|
chunk = decodeChunkMQR(bits_length, bits, qrdata->version);
|
|
} else {
|
|
chunk = decodeChunk(bits_length, bits, qrdata->version);
|
|
}
|
|
if(chunk == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
if(qrdata->last == NULL) {
|
|
qrdata->chunks = chunk;
|
|
} else {
|
|
qrdata->last->next = chunk;
|
|
}
|
|
qrdata->last = chunk;
|
|
|
|
return 0;
|
|
}
|
|
|
|
QRdata *QRdata_new(void)
|
|
{
|
|
QRdata *qrdata;
|
|
|
|
qrdata = (QRdata *)calloc(sizeof(QRdata), 1);
|
|
if(qrdata == NULL) return NULL;
|
|
|
|
qrdata->eccResult = 0;
|
|
|
|
return qrdata;
|
|
}
|
|
|
|
QRdata *QRdata_newMQR(void)
|
|
{
|
|
QRdata *qrdata;
|
|
|
|
qrdata = QRdata_new();
|
|
if(qrdata == NULL) return NULL;
|
|
|
|
qrdata->mqr = 1;
|
|
|
|
return qrdata;
|
|
}
|
|
|
|
void QRdata_free(QRdata *qrdata)
|
|
{
|
|
DataChunk_freeList(qrdata->chunks);
|
|
if(qrdata->data != NULL) {
|
|
free(qrdata->data);
|
|
}
|
|
free(qrdata);
|
|
}
|
|
|
|
static int QRdata_decodeBits(QRdata *qrdata, int length, unsigned char *bits)
|
|
{
|
|
int ret = 0;
|
|
|
|
while(ret == 0) {
|
|
ret = appendChunk(qrdata, &length, &bits);
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
int QRdata_decodeBitStream(QRdata *qrdata, BitStream *bstream)
|
|
{
|
|
return QRdata_decodeBits(qrdata, bstream->length, bstream->data);
|
|
}
|
|
|
|
void QRdata_dump(QRdata *data)
|
|
{
|
|
DataChunk_dumpChunkList(data->chunks);
|
|
}
|
|
|
|
int QRcode_decodeVersion(QRcode *code)
|
|
{
|
|
unsigned int v1, v2;
|
|
int x, y, width;
|
|
unsigned char *p;
|
|
|
|
width = code->width;
|
|
if(width < 45) {
|
|
return (width - 21)/ 4 + 1;
|
|
}
|
|
|
|
v1 = 0;
|
|
p = code->data + width * (width - 9) + 5;
|
|
for(x=0; x<6; x++) {
|
|
for(y=0; y<3; y++) {
|
|
v1 = v1 << 1;
|
|
v1 |= *(p - y * width - x) & 1;
|
|
}
|
|
}
|
|
|
|
v2 = 0;
|
|
p = code->data + width * 5 + width - 9;
|
|
for(y=0; y<6; y++) {
|
|
for(x=0; x<3; x++) {
|
|
v2 = v2 << 1;
|
|
v2 |= *(p - y * width - x) & 1;
|
|
}
|
|
}
|
|
|
|
if(v1 != v2) {
|
|
printf("Two verion patterns are different.\n");
|
|
return -1;
|
|
}
|
|
|
|
return (int)(v1 >> 12);
|
|
}
|
|
|
|
int QRcode_decodeFormat(QRcode *code, QRecLevel *level, int *mask)
|
|
{
|
|
unsigned int v1, v2;
|
|
int i, width;
|
|
unsigned char *p;
|
|
|
|
width = code->width;
|
|
|
|
v1 = 0;
|
|
p = code->data + width * 8;
|
|
for(i=0; i<8; i++) {
|
|
v1 = v1 << 1;
|
|
if(i < 6) {
|
|
v1 |= *(p + i) & 1;
|
|
} else {
|
|
v1 |= *(p + i + 1) & 1;
|
|
}
|
|
}
|
|
p = code->data + width * 7 + 8;
|
|
for(i=0; i<7; i++) {
|
|
v1 = v1 << 1;
|
|
if(i < 1) {
|
|
v1 |= *(p - width * i) & 1;
|
|
} else {
|
|
v1 |= *(p - width * (i + 1)) & 1;
|
|
}
|
|
}
|
|
|
|
v2 = 0;
|
|
p = code->data + width * (width - 1) + 8;
|
|
for(i=0; i<7; i++) {
|
|
v2 = v2 << 1;
|
|
v2 |= *(p - width * i) & 1;
|
|
}
|
|
p = code->data + width * 8 + width - 8;
|
|
for(i=0; i<8; i++) {
|
|
v2 = v2 << 1;
|
|
v2 |= *(p + i) & 1;
|
|
}
|
|
|
|
if(v1 != v2) {
|
|
printf("Two format infos are different.\n");
|
|
return -1;
|
|
}
|
|
v1 = (v1 ^ 0x5412) >> 10;
|
|
*mask = v1 & 7;
|
|
switch((v1 >> 3) & 3) {
|
|
case 1:
|
|
*level = QR_ECLEVEL_L;
|
|
break;
|
|
case 0:
|
|
*level = QR_ECLEVEL_M;
|
|
break;
|
|
case 3:
|
|
*level = QR_ECLEVEL_Q;
|
|
break;
|
|
case 2:
|
|
*level = QR_ECLEVEL_H;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned char *unmask(QRcode *code, QRecLevel level, int mask)
|
|
{
|
|
unsigned char *unmasked;
|
|
|
|
unmasked = Mask_makeMask(code->width, code->data, mask, level);
|
|
|
|
return unmasked;
|
|
}
|
|
|
|
unsigned char *QRcode_unmask(QRcode *code)
|
|
{
|
|
int ret, version, mask;
|
|
QRecLevel level;
|
|
|
|
version = QRcode_decodeVersion(code);
|
|
if(version < 1) return NULL;
|
|
ret = QRcode_decodeFormat(code, &level, &mask);
|
|
if(ret < 0) return NULL;
|
|
|
|
return unmask(code, level, mask);
|
|
}
|
|
|
|
typedef struct {
|
|
int width;
|
|
unsigned char *frame;
|
|
int x, y;
|
|
int dir;
|
|
int bit;
|
|
int mqr;
|
|
} FrameFiller;
|
|
|
|
static FrameFiller *FrameFiller_new(int width, unsigned char *frame, int mqr)
|
|
{
|
|
FrameFiller *filler;
|
|
|
|
filler = (FrameFiller *)malloc(sizeof(FrameFiller));
|
|
if(filler == NULL) return NULL;
|
|
filler->width = width;
|
|
filler->frame = frame;
|
|
filler->x = width - 1;
|
|
filler->y = width - 1;
|
|
filler->dir = -1;
|
|
filler->bit = -1;
|
|
filler->mqr = mqr;
|
|
|
|
return filler;
|
|
}
|
|
|
|
static unsigned char *FrameFiller_next(FrameFiller *filler)
|
|
{
|
|
unsigned char *p;
|
|
int x, y, w;
|
|
|
|
if(filler->bit == -1) {
|
|
filler->bit = 0;
|
|
return filler->frame + filler->y * filler->width + filler->x;
|
|
}
|
|
|
|
x = filler->x;
|
|
y = filler->y;
|
|
p = filler->frame;
|
|
w = filler->width;
|
|
|
|
if(filler->bit == 0) {
|
|
x--;
|
|
filler->bit++;
|
|
} else {
|
|
x++;
|
|
y += filler->dir;
|
|
filler->bit--;
|
|
}
|
|
|
|
if(filler->dir < 0) {
|
|
if(y < 0) {
|
|
y = 0;
|
|
x -= 2;
|
|
filler->dir = 1;
|
|
if(!filler->mqr && x == 6) {
|
|
x--;
|
|
y = 9;
|
|
}
|
|
}
|
|
} else {
|
|
if(y == w) {
|
|
y = w - 1;
|
|
x -= 2;
|
|
filler->dir = -1;
|
|
if(!filler->mqr && x == 6) {
|
|
x--;
|
|
y -= 8;
|
|
}
|
|
}
|
|
}
|
|
if(x < 0 || y < 0) return NULL;
|
|
|
|
filler->x = x;
|
|
filler->y = y;
|
|
|
|
if(p[y * w + x] & 0x80) {
|
|
// This tail recursion could be optimized.
|
|
return FrameFiller_next(filler);
|
|
}
|
|
return &p[y * w + x];
|
|
}
|
|
|
|
static BitStream *extractBits(int width, unsigned char *frame, int spec[5])
|
|
{
|
|
BitStream *bstream;
|
|
unsigned char *bits, *p, *q;
|
|
FrameFiller *filler;
|
|
int i, j;
|
|
int col, row, d1, b1, blocks, idx, words;
|
|
|
|
blocks = QRspec_rsBlockNum(spec);
|
|
words = QRspec_rsDataLength(spec);
|
|
d1 = QRspec_rsDataCodes1(spec);
|
|
b1 = QRspec_rsBlockNum1(spec);
|
|
bits = (unsigned char *)malloc((size_t)words * 8);
|
|
/*
|
|
* 00 01 02 03 04 05 06 07 08 09
|
|
* 10 11 12 13 14 15 16 17 18 19
|
|
* 20 21 22 23 24 25 26 27 28 29 30
|
|
* 31 32 33 34 35 36 37 38 39 40 41
|
|
* 42 43 44 45 46 47 48 49 50 51 52
|
|
*/
|
|
|
|
row = col = 0;
|
|
filler = FrameFiller_new(width, frame, 0);
|
|
for(i=0; i<words; i++) {
|
|
col = i / blocks;
|
|
row = i % blocks + ((col >= d1)?b1:0);
|
|
idx = d1 * row + col + ((row > b1)?(row-b1):0);
|
|
q = bits + idx * 8;
|
|
for(j=0; j<8; j++) {
|
|
p = FrameFiller_next(filler);
|
|
q[j] = *p & 1;
|
|
}
|
|
}
|
|
free(filler);
|
|
|
|
bstream = BitStream_newWithBits((size_t)words * 8, bits);
|
|
free(bits);
|
|
|
|
return bstream;
|
|
}
|
|
|
|
BitStream *QRcode_extractBits(QRcode *code, int *dataLength, int *eccLength)
|
|
{
|
|
BitStream *bstream;
|
|
unsigned char *unmasked;
|
|
int spec[5];
|
|
int ret, version, mask;
|
|
QRecLevel level;
|
|
|
|
version = QRcode_decodeVersion(code);
|
|
if(version < 1) return NULL;
|
|
ret = QRcode_decodeFormat(code, &level, &mask);
|
|
if(ret < 0) return NULL;
|
|
|
|
QRspec_getEccSpec(version, level, spec);
|
|
|
|
unmasked = unmask(code, level, mask);
|
|
if(unmasked == NULL) return NULL;
|
|
|
|
bstream = extractBits(code->width, unmasked, spec);
|
|
free(unmasked);
|
|
|
|
*dataLength = QRspec_rsDataLength(spec) * 8;
|
|
*eccLength = QRspec_rsEccLength(spec) * 8;
|
|
|
|
return bstream;
|
|
}
|
|
|
|
static int checkRemainderWords(int length, unsigned char *bits, int remainder)
|
|
{
|
|
int rbits, words;
|
|
unsigned char *p, v;
|
|
int i;
|
|
|
|
words = remainder / 8;
|
|
rbits = remainder - words * 8;
|
|
bits += (length - remainder);
|
|
for(i=0; i<rbits; i++) {
|
|
if((bits[i]&1) != 0) {
|
|
printf("Terminating code includes 1-bit.\n");
|
|
printBinary(bits, remainder);
|
|
return -1;
|
|
}
|
|
}
|
|
p = bits + rbits;
|
|
for(i=0; i<words; i++) {
|
|
v = (unsigned char)bitToInt(p, 8);
|
|
if(v != ((i&1)?0x11:0xec)) {
|
|
printf("Remainder codewords wrong.\n");
|
|
printBinary(bits, remainder);
|
|
return -1;
|
|
}
|
|
p += 8;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
QRdata *QRcode_decodeBits(QRcode *code)
|
|
{
|
|
BitStream *bstream;
|
|
unsigned char *unmasked;
|
|
int spec[5];
|
|
int ret, version, mask;
|
|
int length;
|
|
QRecLevel level;
|
|
QRdata *qrdata;
|
|
|
|
version = QRcode_decodeVersion(code);
|
|
if(version < 1) return NULL;
|
|
ret = QRcode_decodeFormat(code, &level, &mask);
|
|
if(ret < 0) return NULL;
|
|
|
|
QRspec_getEccSpec(version, level, spec);
|
|
length = QRspec_rsDataLength(spec) * 8;
|
|
|
|
unmasked = unmask(code, level, mask);
|
|
if(unmasked == NULL) return NULL;
|
|
|
|
bstream = extractBits(code->width, unmasked, spec);
|
|
free(unmasked);
|
|
|
|
qrdata = QRdata_new();
|
|
qrdata->version = version;
|
|
qrdata->level = level;
|
|
ret = QRdata_decodeBitStream(qrdata, bstream);
|
|
if(ret > 0) {
|
|
checkRemainderWords(length, bstream->data, ret);
|
|
}
|
|
|
|
BitStream_free(bstream);
|
|
|
|
return qrdata;
|
|
}
|
|
|
|
void QRdata_concatChunks(QRdata *qrdata)
|
|
{
|
|
qrdata->data = DataChunk_concatChunkList(qrdata->chunks, &qrdata->size);
|
|
}
|
|
|
|
QRdata *QRcode_decode(QRcode *code)
|
|
{
|
|
QRdata *qrdata;
|
|
|
|
qrdata = QRcode_decodeBits(code);
|
|
QRdata_concatChunks(qrdata);
|
|
|
|
return qrdata;
|
|
}
|
|
|
|
/*
|
|
* Micro QR Code decoder
|
|
*/
|
|
|
|
struct FormatInfo MQRformat[] = {
|
|
{1, QR_ECLEVEL_L},
|
|
{2, QR_ECLEVEL_L},
|
|
{2, QR_ECLEVEL_M},
|
|
{3, QR_ECLEVEL_L},
|
|
{3, QR_ECLEVEL_M},
|
|
{4, QR_ECLEVEL_L},
|
|
{4, QR_ECLEVEL_M},
|
|
{4, QR_ECLEVEL_Q}
|
|
};
|
|
|
|
int QRcode_decodeFormatMQR(QRcode *code, int *version, QRecLevel *level, int *mask)
|
|
{
|
|
unsigned int v, t;
|
|
int i, width;
|
|
unsigned char *p;
|
|
|
|
width = code->width;
|
|
|
|
v = 0;
|
|
p = code->data + width * 8 + 1;
|
|
for(i=0; i<8; i++) {
|
|
v = v << 1;
|
|
v |= p[i] & 1;
|
|
}
|
|
p = code->data + width * 7 + 8;
|
|
for(i=0; i<7; i++) {
|
|
v = v << 1;
|
|
v |= *(p - width * i) & 1;
|
|
}
|
|
v ^= 0x4445;
|
|
*mask = (v >> 10) & 3;
|
|
t = (v >> 12) & 7;
|
|
*version = MQRformat[t].version;
|
|
*level = MQRformat[t].level;
|
|
if(*version * 2 + 9 != width) {
|
|
printf("Decoded version number does not match to the size.\n");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static unsigned char *unmaskMQR(QRcode *code, QRecLevel level, int mask)
|
|
{
|
|
unsigned char *unmasked;
|
|
|
|
unmasked = MMask_makeMask(code->version, code->data, mask, level);
|
|
|
|
return unmasked;
|
|
}
|
|
|
|
unsigned char *QRcode_unmaskMQR(QRcode *code)
|
|
{
|
|
int ret, version, mask;
|
|
QRecLevel level;
|
|
|
|
ret = QRcode_decodeFormatMQR(code, &version, &level, &mask);
|
|
if(ret < 0) return NULL;
|
|
|
|
return unmaskMQR(code, level, mask);
|
|
}
|
|
|
|
static BitStream *extractBitsMQR(int width, unsigned char *frame, int version, QRecLevel level)
|
|
{
|
|
BitStream *bstream;
|
|
unsigned char *bits;
|
|
FrameFiller *filler;
|
|
int i;
|
|
int size;
|
|
|
|
size = MQRspec_getDataLengthBit(version, level) + MQRspec_getECCLength(version, level) * 8;
|
|
bits = (unsigned char *)malloc((size_t)size);
|
|
filler = FrameFiller_new(width, frame, 1);
|
|
for(i=0; i<size; i++) {
|
|
bits[i] = *(FrameFiller_next(filler)) & 1;
|
|
}
|
|
free(filler);
|
|
|
|
bstream = BitStream_newWithBits((size_t)size, bits);
|
|
free(bits);
|
|
|
|
return bstream;
|
|
}
|
|
|
|
BitStream *QRcode_extractBitsMQR(QRcode *code, int *dataLength, int *eccLength, int *version, QRecLevel *level)
|
|
{
|
|
BitStream *bstream;
|
|
unsigned char *unmasked;
|
|
int ret, mask;
|
|
|
|
ret = QRcode_decodeFormatMQR(code, version, level, &mask);
|
|
if(ret < 0) return NULL;
|
|
|
|
unmasked = unmaskMQR(code, *level, mask);
|
|
if(unmasked == NULL) return NULL;
|
|
|
|
*dataLength = MQRspec_getDataLengthBit(*version, *level);
|
|
*eccLength = MQRspec_getECCLength(*version, *level) * 8;
|
|
bstream = extractBitsMQR(code->width, unmasked, *version, *level);
|
|
free(unmasked);
|
|
|
|
return bstream;
|
|
}
|
|
|
|
static int checkRemainderWordsMQR(int length, unsigned char *bits, int remainder, int version)
|
|
{
|
|
int rbits, words, paddings;
|
|
unsigned char *p, v;
|
|
int i, decoded;
|
|
|
|
decoded = length - remainder;
|
|
bits += decoded;
|
|
words = (decoded + 7) / 8;
|
|
rbits = words * 8 - decoded;
|
|
for(i=0; i<rbits; i++) {
|
|
if((bits[i]&1) != 0) {
|
|
printf("Terminating code includes 1-bit.\n");
|
|
printBinary(bits, remainder);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
paddings = (length - words * 8) / 8;
|
|
p = bits + rbits;
|
|
for(i=0; i<paddings; i++) {
|
|
v = (unsigned char)bitToInt(p, 8);
|
|
if(v != ((i&1)?0x11:0xec)) {
|
|
printf("Remainder codewords wrong.\n");
|
|
printBinary(bits, remainder);
|
|
return -1;
|
|
}
|
|
p += 8;
|
|
}
|
|
rbits = length - (paddings + words)* 8;
|
|
if(rbits > 0) {
|
|
if((version == 1 || version == 3) && rbits == 4) {
|
|
v = (unsigned char)bitToInt(p, 4);
|
|
if(v != 0) {
|
|
printf("Last padding bits include 1-bit.\n");
|
|
return -1;
|
|
}
|
|
} else {
|
|
printf("The length of the last padding bits is %d, not %d.\n", rbits, (version == 1 || version == 3)?4:0);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
QRdata *QRcode_decodeBitsMQR(QRcode *code)
|
|
{
|
|
BitStream *bstream;
|
|
int ret, version, dataLength, eccLength;
|
|
QRecLevel level;
|
|
QRdata *qrdata;
|
|
|
|
bstream = QRcode_extractBitsMQR(code, &dataLength, &eccLength, &version, &level);
|
|
if(bstream == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
qrdata = QRdata_newMQR();
|
|
qrdata->version = version;
|
|
qrdata->level = level;
|
|
ret = QRdata_decodeBits(qrdata, dataLength, bstream->data);
|
|
if(ret > 0) {
|
|
ret = checkRemainderWordsMQR(dataLength, bstream->data, ret, version);
|
|
if(ret < 0) {
|
|
QRdata_free(qrdata);
|
|
qrdata = NULL;
|
|
}
|
|
}
|
|
|
|
BitStream_free(bstream);
|
|
|
|
return qrdata;
|
|
}
|
|
|
|
QRdata *QRcode_decodeMQR(QRcode *code)
|
|
{
|
|
QRdata *qrdata;
|
|
qrdata = QRcode_decodeBitsMQR(code);
|
|
if(qrdata != NULL) {
|
|
QRdata_concatChunks(qrdata);
|
|
}
|
|
|
|
return qrdata;
|
|
}
|