/*
|
* Copyright 2012 ZXing authors
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*/
|
|
#import "ZXBitMatrix.h"
|
#import "ZXByteArray.h"
|
#import "ZXDecodeHints.h"
|
#import "ZXDecoderResult.h"
|
#import "ZXErrors.h"
|
#import "ZXGenericGF.h"
|
#import "ZXIntArray.h"
|
#import "ZXMaxiCodeBitMatrixParser.h"
|
#import "ZXMaxiCodeDecodedBitStreamParser.h"
|
#import "ZXMaxiCodeDecoder.h"
|
#import "ZXReedSolomonDecoder.h"
|
|
const int ZX_MAXI_CODE_ALL = 0;
|
const int ZX_MAXI_CODE_EVEN = 1;
|
const int ZX_MAXI_CODE_ODD = 2;
|
|
@interface ZXMaxiCodeDecoder ()
|
|
@property (nonatomic, strong, readonly) ZXReedSolomonDecoder *rsDecoder;
|
|
@end
|
|
@implementation ZXMaxiCodeDecoder
|
|
- (id)init {
|
if (self = [super init]) {
|
_rsDecoder = [[ZXReedSolomonDecoder alloc] initWithField:[ZXGenericGF MaxiCodeField64]];
|
}
|
|
return self;
|
}
|
|
- (ZXDecoderResult *)decode:(ZXBitMatrix *)bits error:(NSError **)error {
|
return [self decode:bits hints:nil error:error];
|
}
|
|
- (ZXDecoderResult *)decode:(ZXBitMatrix *)bits hints:(ZXDecodeHints *)hints error:(NSError **)error {
|
ZXMaxiCodeBitMatrixParser *parser = [[ZXMaxiCodeBitMatrixParser alloc] initWithBitMatrix:bits error:error];
|
if (!parser) {
|
return nil;
|
}
|
ZXByteArray *codewords = [parser readCodewords];
|
|
if (![self correctErrors:codewords start:0 dataCodewords:10 ecCodewords:10 mode:ZX_MAXI_CODE_ALL error:error]) {
|
return nil;
|
}
|
int mode = codewords.array[0] & 0x0F;
|
ZXByteArray *datawords;
|
switch (mode) {
|
case 2:
|
case 3:
|
case 4:
|
if (![self correctErrors:codewords start:20 dataCodewords:84 ecCodewords:40 mode:ZX_MAXI_CODE_EVEN error:error]) {
|
return nil;
|
}
|
if (![self correctErrors:codewords start:20 dataCodewords:84 ecCodewords:40 mode:ZX_MAXI_CODE_ODD error:error]) {
|
return nil;
|
}
|
datawords = [[ZXByteArray alloc] initWithLength:94];
|
break;
|
case 5:
|
if (![self correctErrors:codewords start:20 dataCodewords:68 ecCodewords:56 mode:ZX_MAXI_CODE_EVEN error:error]) {
|
return nil;
|
}
|
if (![self correctErrors:codewords start:20 dataCodewords:68 ecCodewords:56 mode:ZX_MAXI_CODE_ODD error:error]) {
|
return nil;
|
}
|
datawords = [[ZXByteArray alloc] initWithLength:78];
|
break;
|
default:
|
if (error) *error = ZXNotFoundErrorInstance();
|
return nil;
|
}
|
|
for (int i = 0; i < 10; i++) {
|
datawords.array[i] = codewords.array[i];
|
}
|
for (int i = 20; i < datawords.length + 10; i++) {
|
datawords.array[i - 10] = codewords.array[i];
|
}
|
|
return [ZXMaxiCodeDecodedBitStreamParser decode:datawords mode:mode];
|
}
|
|
- (BOOL)correctErrors:(ZXByteArray *)codewordBytes start:(int)start dataCodewords:(int)dataCodewords
|
ecCodewords:(int)ecCodewords mode:(int)mode error:(NSError **)error {
|
int codewords = dataCodewords + ecCodewords;
|
|
// in EVEN or ODD mode only half the codewords
|
int divisor = mode == ZX_MAXI_CODE_ALL ? 1 : 2;
|
|
// First read into an array of ints
|
ZXIntArray *codewordsInts = [[ZXIntArray alloc] initWithLength:codewords / divisor];
|
for (int i = 0; i < codewords; i++) {
|
if ((mode == ZX_MAXI_CODE_ALL) || (i % 2 == (mode - 1))) {
|
codewordsInts.array[i / divisor] = codewordBytes.array[i + start] & 0xFF;
|
}
|
}
|
|
NSError *decodeError = nil;
|
if (![self.rsDecoder decode:codewordsInts twoS:ecCodewords / divisor error:&decodeError]) {
|
if (decodeError.code == ZXReedSolomonError && error) {
|
*error = ZXChecksumErrorInstance();
|
}
|
return NO;
|
}
|
// Copy back into array of bytes -- only need to worry about the bytes that were data
|
// We don't care about errors in the error-correction codewords
|
for (int i = 0; i < dataCodewords; i++) {
|
if ((mode == ZX_MAXI_CODE_ALL) || (i % 2 == (mode - 1))) {
|
codewordBytes.array[i + start] = (int8_t) codewordsInts.array[i / divisor];
|
}
|
}
|
|
return YES;
|
}
|
|
@end
|