diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..67bb9fd --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +MagRead.pro.user +.*swp diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..b85ee96 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,44 @@ +magread (0.1.5) stable; urgency=low + + * Fixed a bug (#1) in displaying driver licence/id numbers from some states + * Fixed a formatting issue (#7) for bank cards read from track 2 + * Fixed a bug (#9) that could cause a bad loop of failed reads that could + prevent the user from easily stopping the process. + * Create a settings page + * Allow user to turn on/off auto-rotation on Maemo + * Create setting for always formatting or not formatting AAMVA and bank + cards + * Add support for setting normalization, silence and timeout variables + * Add support for selecting the input audio device + * Add support for choosing a decoding algorithm + + -- Jeffrey Malone Wed, 1 Dec 2010 15:19:00 -0700 + +magread (0.1.3) stable; urgency=low + + * Moved entirely over to Qt. Now requires PR1.3 to successfully read cards. + * Added support for Symbian, Windows, OS X and Linux + * Supports auto-rotate functionality; redraws the screen to suite current + orientation. + * Fixed several bugs + * Added an about dialogue that includes a version number and a link to my + blog. + * Moved all issue tracking from the Maemo Garage to github because it is now + a multiplatform program. + + -- Jeffrey Malone Sun, 28 Nov 2010 22:56:47 -0700 + +magread (0.1.1) unstable; urgency=low + + * Fixed missing icon + * Rotated icon 180 degrees + * Fixed a leap year issue for calculating ages that could result in 1 day + age skew. + + -- Jeffrey Malone Sat, 6 Nov 2010 16:06:47 -0700 + +magread (0.1.0) unstable; urgency=low + + * Initial release + + -- Jeffrey Malone Thu, 04 Nov 2010 06:10:14 -0700 diff --git a/MagRead.pro b/MagRead.pro index 41f6c9c..acda23f 100644 --- a/MagRead.pro +++ b/MagRead.pro @@ -13,7 +13,7 @@ maemo5 { TARGET = MagRead TEMPLATE = app -VERSION = 0.1.3 +VERSION = 0.1.5 DEFINES += APP_VERSION=$$VERSION SOURCES += main.cpp\ @@ -23,7 +23,8 @@ SOURCES += main.cpp\ mslib.c \ llist.c \ accountcard.cpp \ - aamvacard.cpp + aamvacard.cpp \ + settingspage.cpp HEADERS += magread.h \ carddetect.h \ @@ -32,7 +33,8 @@ HEADERS += magread.h \ llist.h \ magcard.h \ accountcard.h \ - aamvacard.h + aamvacard.h \ + settingspage.h symbian { TARGET.UID3 = 0xe2c961e1 @@ -41,3 +43,12 @@ symbian { TARGET.EPOCHEAPSIZE = 0x020000 0x800000 ICON = "magread.svg" } + +unix { + INSTALLS += target desktop icon48 + target.path = /usr/bin/magread + desktop.path = /usr/share/applications/hildon + desktop.files += maemofiles/magread.desktop + icon48.path = /usr/share/icons/hicolor/48x48/hildon + icon48.files += maemofiles/magread.png +} diff --git a/README b/README index 9a4b223..28c91cd 100644 --- a/README +++ b/README @@ -1,23 +1,45 @@ MagRead is an application to read magnetic stripe cards. +It was written by myself, Jeffrey Malone as a fun app to demonstrate the mslib +library that decodes the magnetic stripe data itself. +MagRead should be able to read any magnetic stripe that conforms to the +standards defined by the ABA and the IATA. These two formats are by far the +predominant ones found, and the only exceptions I've personally run across are +hotel keys. + Requirements: - A hardware audio dongle to read the cards, such as those provided by Square for their payment service. + - A compatible audio jack on the device. Compatible jacks are ones that have + a pin-out for audio output and input -- typically headset jacks. These are + found on some newer MacBooks and Thinkpads, along with many other devices. - Qt 4.6 or later (including Qt Multimedia plugin) -It may run on any device meeting the above requirements, but has only been -tested on a Linux desktop machine, a Nokia N8 and a Nokia N900. +Confirmed to work on: +Mac OS X 10.6 on a current MacBook Pro +Linux on a Thinkpad W510 +Windows 7 on a Thinkpad W510 +Nokia N8 with Symbian S^3 +Nokia N900 with Maemo 5 PR 1.3 (For setFont( medFont ); @@ -67,8 +72,8 @@ AAMVACard::AAMVACard( MagCard *_card ) { } void AAMVACard::reorient() { -// QSize geometry = size(); - QRect geometry = QApplication::desktop()->screenGeometry(); + QSize geometry = size(); +// QRect geometry = QApplication::desktop()->screenGeometry(); if( geometry.width() > geometry.height() ) { //landscape diff --git a/accountcard.cpp b/accountcard.cpp index 6e11b4f..23d1970 100644 --- a/accountcard.cpp +++ b/accountcard.cpp @@ -8,16 +8,19 @@ AccountCard::AccountCard( MagCard *_card ) { layout = new QVBoxLayout; setLayout( layout ); - //smallFont.setPointSize( 12 ); - //medFont.setPointSize( 16 ); +#ifdef Q_WS_MAEMO_5 smallFont.setPointSize( 32 ); medFont.setPointSize( 32 ); +#else + smallFont.setPointSize( 12 ); + medFont.setPointSize( 16 ); +#endif accountNumber = new QLabel; layout->addWidget( accountNumber, 1, Qt::AlignHCenter ); accountHolder = new QLabel; - accountHolder->setFont( medFont ); + accountHolder->setFont( smallFont ); layout->addWidget( accountHolder, 1, Qt::AlignHCenter ); label = new QLabel( "Expiration Date" ); @@ -58,18 +61,26 @@ AccountCard::AccountCard( MagCard *_card ) { if( _card ) { card = _card; showData(); - } + }; } void AccountCard::reorient() { -// QSize geometry = size(); +#ifdef Q_OS_SYMBIAN + // for some reason, this works only on symbian, and the else works on everything but symbian QRect geometry = QApplication::desktop()->screenGeometry(); +#else + QSize geometry = size(); +#endif if( geometry.width() > geometry.height() ) { //landscape if( orientation != LANDSCAPE ) { qDebug() << "Landscape Mode"; - accountNumberFont.setPointSize( 48 );//18 +#ifdef Q_WS_MAEMO_5 + accountNumberFont.setPointSize( 48 ); +#else + accountNumberFont.setPointSize( 18 ); +#endif accountNumber->setFont( accountNumberFont ); layout->removeItem( layout->itemAt( 2 ) ); @@ -81,7 +92,11 @@ void AccountCard::reorient() { //portrait if( orientation != PORTRAIT ) { qDebug() << "Portrait Mode"; - accountNumberFont.setPointSize( 32 );//12 +#ifdef Q_WS_MAEMO_5 + accountNumberFont.setPointSize( 32 ); +#else + accountNumberFont.setPointSize( 12 ); +#endif accountNumber->setFont( accountNumberFont ); layout->removeItem( layout->itemAt( 2 ) ); @@ -123,8 +138,10 @@ void AccountCard::showData() { if( !card->accountHolder.isEmpty() ) accountHolder->setText( card->accountHolder ); - else + else { + accountHolder->hide(); accountHolder->clear(); + } /* Expiration Date */ tmpStr = card->expirationDate.toString( "MMM dd, yyyy" ); diff --git a/carddetect.cpp b/carddetect.cpp index 3635dfc..cdc0472 100644 --- a/carddetect.cpp +++ b/carddetect.cpp @@ -187,7 +187,7 @@ void CardDetect::aamvaCardCheck( QString expDate ) { card->accountNumber.remove( iin ); if( card->miscData.length() > 8 ) card->accountNumber.append( card->miscData.mid( 8 ) ); - + //format the id number if applicable if( !issuerInfo.format.isEmpty() ) { for( int i = 0; i < issuerInfo.format.length(); i++ ) { @@ -199,10 +199,14 @@ void CardDetect::aamvaCardCheck( QString expDate ) { card->accountNumber.replace( i, 2, letter ); } else if( issuerInfo.format.at( i ) != 'N' ) { card->accountNumber.insert( i, issuerInfo.format.at( i ) ); + } else if( card->accountNumber.at( i ) == '=' ) { + card->accountNumber.replace( i, 1, '0' ); } } } + card->accountNumber.remove( '=' ); + //set the birthday QString bday = card->miscData.left( 8 ); if( bday.mid( 4, 2 ) > "12" ) { //some (Calif) violate AAMVA standard and switch the exp and bday month values diff --git a/debian/changelog b/debian/changelog deleted file mode 100644 index ed50c51..0000000 --- a/debian/changelog +++ /dev/null @@ -1,14 +0,0 @@ -magread (0.1.1) unstable; urgency=low - - * Fixed missing icon - * Rotated icon 180 degrees - * Fixed a leap year issue for calculating ages that could result in 1 day - age skew. - - -- Jeffrey Malone Sat, 6 Nov 2010 16:06:47 -0700 - -magread (0.1.0) unstable; urgency=low - - * Initial release - - -- Jeffrey Malone Thu, 04 Nov 2010 06:10:14 -0700 diff --git a/debian/changelog b/debian/changelog new file mode 120000 index 0000000..22ec9b8 --- /dev/null +++ b/debian/changelog @@ -0,0 +1 @@ +../ChangeLog \ No newline at end of file diff --git a/debian/control b/debian/control index ec50faa..1e07eca 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: magread Section: user/utilities Priority: extra Maintainer: Jeffrey Malone -Build-Depends: debhelper (>= 5), libqt4-dev (>=4.6.1), libpulse-dev +Build-Depends: debhelper (>= 5), libqt4-dev (>=4.6.1), libqt4-multimedia (>=4.6.1) Standards-Version: 3.7.3 Package: magread @@ -16,7 +16,7 @@ Description: Reads magnetic stripe cards data and display it in a familiar format. *Requires that you have a hardware dongle, such as those provided by Square.* See http://blog.tehinterweb.com/ for more details -XSBC-Bugtracker: https://garage.maemo.org/tracker/?group_id=1941 +XSBC-Bugtracker: https://github.com/ieatlint/MagRead/issues XB-Maemo-Icon-26: iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c 6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0 diff --git a/llist.c b/llist.c index e9dc05b..0630db9 100644 --- a/llist.c +++ b/llist.c @@ -46,7 +46,7 @@ void llist_remove_idx( LListH *list, int idx ) { prev = trav; } - if( trav != NULL ) {//if found + if( trav != NULL ) {/* if found */ if( prev != NULL ) { prev->next = trav->next; } else { diff --git a/magcard.h b/magcard.h index d72ceb2..6738bf9 100644 --- a/magcard.h +++ b/magcard.h @@ -66,6 +66,13 @@ class MagCard { swipeValid = false; } + void clear() { + type = CARD_UNSET; + charStream.clear(); + bitStream.clear(); + accountNumber.clear(); + } + }; Q_DECLARE_OPERATORS_FOR_FLAGS( MagCard::Types ) diff --git a/magdecode.cpp b/magdecode.cpp index 61a532d..db698ff 100644 --- a/magdecode.cpp +++ b/magdecode.cpp @@ -11,6 +11,11 @@ MagDecode::MagDecode(QObject *parent) : QIODevice(parent) { normOffsetFound = false; normOffset = 0; + + defaultTimeOut = 10; //default to 10 + timeOut = defaultTimeOut; + + algorithm = "walk"; } void MagDecode::start() { @@ -25,6 +30,11 @@ qint64 MagDecode::writeData( const char *data, qint64 dataLen ) { if( !captureAudio ) return dataLen; + if( timeOut > 0 ) { + timeOut--; + return dataLen; + } + const qint16 *pcmDataBlock = reinterpret_cast( data ); int blockLen = dataLen / sizeof( qint16 ); @@ -68,6 +78,8 @@ qint64 MagDecode::writeData( const char *data, qint64 dataLen ) { void MagDecode::processSwipe() { bool valid; + timeOut = defaultTimeOut; + //Normalize the audio based on calculated 0 level for( int i = 0; i < pcmData.count(); i++ ) pcmData[ i ] -= normOffset; @@ -76,7 +88,14 @@ void MagDecode::processSwipe() { ms_set_peakThreshold( ms, silenceThreshold ); - ms_peaks_find_walk( ms ); + if( algorithm == "intersect" ) { + ms_peaks_find( ms ); + qDebug() << "Intersection algorithm used"; + } else { + ms_peaks_find_walk( ms ); + qDebug() << "Walk algorithm used"; + } + ms_peaks_filter_group( ms ); // ms_save( ms, "/tmp/swipe" ); @@ -116,3 +135,19 @@ void MagDecode::setThreshold( int threshold ) { silenceThreshold = threshold; } +void MagDecode::setTimeOut( int _timeOut ) { + defaultTimeOut = _timeOut; +} + +void MagDecode::setNorm( int _normOffset ) { + normOffsetFound = true; + normOffset = _normOffset; +} + +void MagDecode::setAlgorithm( QString _algorithm ) { + qDebug() << "Settings algorithm to" << _algorithm; + if( _algorithm == "intersect" ) + algorithm = _algorithm; + else + algorithm = "walk"; +} diff --git a/magdecode.h b/magdecode.h index d3d2493..a5d0bef 100644 --- a/magdecode.h +++ b/magdecode.h @@ -17,17 +17,25 @@ class MagDecode : public QIODevice void stop(); qint64 writeData( const char *data, qint64 dataLen ); qint64 readData( char *data, qint64 len ); + + void setTimeOut( int _timeOut ); + void setNorm( int _normOffset ); + void setAlgorithm( QString _algorithm ); private: int silenceCount; bool noiseDetected; bool captureAudio; + int timeOut; + int defaultTimeOut; void processSwipe(); int silenceThreshold; int normOffset; bool normOffsetFound; + QString algorithm; + QVarLengthArray pcmData; signals: diff --git a/magread.cpp b/magread.cpp index e36ca91..94ffb6d 100644 --- a/magread.cpp +++ b/magread.cpp @@ -36,12 +36,22 @@ MagRead::MagRead(QWidget *parent) : QMainWindow(parent) { qRegisterMetaType( "MagCard" ); + settings = new QSettings; + //Set the auto-rotation for Maemo5 #ifdef Q_WS_MAEMO_5 - setAttribute(Qt::WA_Maemo5AutoOrientation, true); + if( settings->value( "autoReorient" ) == true ) { + setAttribute( Qt::WA_Maemo5AutoOrientation, true ); + } #endif +#ifdef Q_WS_MAEMO_5 + font.setPointSize( 32 ); +#else + font.setPointSize( 16 ); +#endif + // Start/stop/back selections #ifdef Q_OS_SYMBIAN backSoftKey = new QAction( "Start", this ); @@ -64,12 +74,16 @@ MagRead::MagRead(QWidget *parent) : QMainWindow(parent) { #ifndef Q_OS_SYMBIAN settingsAction = new QAction( "&Settings", this ); + connect( settingsAction, SIGNAL( triggered() ), this, SLOT( settingsPage() ) ); aboutAction = new QAction( "&About", this ); connect( aboutAction, SIGNAL( triggered() ), this, SLOT( aboutDialogue() ) ); exitAction = new QAction( "E&xit", this ); connect( exitAction, SIGNAL( triggered() ), this, SLOT( close() ) ); + + showDataAction = new QAction( "Show &Data", this ); + connect( showDataAction, SIGNAL( triggered() ), this, SLOT( showData() ) ); #endif #ifdef Q_WS_MAEMO_5 @@ -77,7 +91,7 @@ MagRead::MagRead(QWidget *parent) : QMainWindow(parent) { menuBar()->addAction( aboutAction ); menuBar()->addAction( exitAction ); #elif !defined( Q_OS_SYMBIAN ) - QMenu *fileMenu = menuBar()->addMenu( "&File" ); + fileMenu = menuBar()->addMenu( "&File" ); fileMenu->addAction( settingsAction ); fileMenu->addAction( aboutAction ); @@ -123,14 +137,27 @@ void MagRead::notice( QString msg, int timeout, mboxStatus status ) { } +void MagRead::showData() { + if( !card.charStream.isEmpty() ) { + miscPage(); + } +} + void MagRead::cardRead( const MagCard _card ) { card = _card; cardDetect.setCard( &card ); if( ( card.type & MagCard::CARD_CC || card.type == MagCard::CARD_AAA ) && card.accountValid ) { - creditPage(); + if( settings->value( "formatCredit" ) == true ) + creditPage(); + else + miscPage(); + } else if( card.type == MagCard::CARD_AAMVA ) { - aamvaPage(); + if( settings->value( "formatAAMVA" ) == true ) + aamvaPage(); + else + miscPage(); } else if( card.swipeValid ) { miscPage(); } else if( partialRead && !card.charStream.isEmpty() ) { @@ -149,7 +176,8 @@ void MagRead::mainPage() { widget->setLayout( layout ); QLabel *label; - label = new QLabel( "MagRead" ); + label = new QLabel( "MagRead" ); + label->setFont( font ); layout->addWidget( label, 1, Qt::AlignHCenter ); QCheckBox *cbox = new QCheckBox( "Show Partial Data" ); @@ -163,6 +191,7 @@ void MagRead::mainPage() { QPushButton *settingsBtn = new QPushButton( "Settings" ); optionsBox->addWidget( settingsBtn ); + connect( settingsBtn, SIGNAL( clicked() ), this, SLOT( settingsPage() ) ); QPushButton *aboutBtn = new QPushButton( "About" ); optionsBox->addWidget( aboutBtn ); @@ -179,6 +208,8 @@ void MagRead::mainPage() { mainLayout->insertWidget( 0, widget, 1 ); #endif + removeShowData(); + } void MagRead::aboutDialogue() { @@ -204,6 +235,7 @@ void MagRead::toggleRead() { backStr = "Stop"; captureAudio = true; } else { + card.clear(); backStr = "Start"; mainPage(); } @@ -220,7 +252,27 @@ void MagRead::captureStart() { connect( magDec, SIGNAL( cardRead( MagCard ) ), this, SLOT( cardRead( MagCard ) ) ); connect( magDec, SIGNAL( errorMsg( QString ) ), this, SLOT( notice( QString ) ) ); - audioInput = new QAudioInput( audioFormat, this ); + magDec->setThreshold( settings->value( "silenceThreshold" ).toInt() ); + magDec->setTimeOut( settings->value( "timeOut" ).toInt() ); + + if( settings->value( "normAuto" ) == false ) + magDec->setNorm( settings->value( "norm" ).toInt() ); + + magDec->setAlgorithm( settings->value( "algorithm" ).toString() ); + + audioInput = 0; + + QList inputDevices = QAudioDeviceInfo::availableDevices( QAudio::AudioInput ); + for( int i = 0; i < inputDevices.size(); i++ ) { + if( inputDevices.at( i ).deviceName() == settings->value( "audioDevice" ) ) { + audioInput = new QAudioInput( inputDevices.at( i ), audioFormat, this ); + } + } + + if( audioInput == 0 ) + audioInput = new QAudioInput( audioFormat, this ); + + magDec->start(); audioInput->start( magDec ); } @@ -241,6 +293,59 @@ void MagRead::togglePartialRead( bool _partialRead ) { partialRead = _partialRead; } +void MagRead::addShowData() { +#ifdef Q_WS_MAEMO_5 + if( !menuBar()->actions().contains( showDataAction ) ) + menuBar()->addAction( showDataAction ); +#elif !defined( Q_OS_SYMBIAN ) + if( !fileMenu->actions().contains( showDataAction ) ) + fileMenu->addAction( showDataAction ); +#endif +} + +void MagRead::removeShowData() { +#ifdef Q_WS_MAEMO_5 + if( menuBar()->actions().contains( showDataAction ) ) + menuBar()->removeAction( showDataAction ); +#elif !defined( Q_OS_SYMBIAN ) + if( fileMenu->actions().contains( showDataAction ) ) + fileMenu->removeAction( showDataAction ); +#endif +} + +void MagRead::settingsPage() { + onMainPage = false; + + SettingsPage *settingsWidget = new SettingsPage; + connect( settingsWidget, SIGNAL( autoReorientSig( bool ) ), this, SLOT( autoReorient( bool ) ) ); + +#ifdef Q_OS_SYMBIAN + setCentralWidget( settingsWidget ); +#else + if( mainLayout->count() > 1 ) { + mainLayout->itemAt( 0 )->widget()->hide(); + mainLayout->removeItem( mainLayout->itemAt( 0 ) ); + } + mainLayout->insertWidget( 0, settingsWidget, 1 ); +#endif + +#ifdef Q_OS_SYMBIAN + backSoftKey->setText( "Back" ); +#else + mainBackBtn->setText( "Back" ); +#endif + + removeShowData(); +} + +void MagRead::autoReorient( bool enabled ) { +#ifdef Q_WS_MAEMO_5 + setAttribute( Qt::WA_Maemo5AutoOrientation, enabled ); +#else + Q_UNUSED( enabled ); +#endif +} + /* Credit Page */ void MagRead::creditPage() { notice( "Successfully Read Credit Card", 750 ); @@ -258,6 +363,9 @@ void MagRead::creditPage() { } mainLayout->insertWidget( 0, accountCard, 1 ); #endif + + addShowData(); + } void MagRead::aamvaPage() { @@ -276,6 +384,8 @@ void MagRead::aamvaPage() { } mainLayout->insertWidget( 0, aamvaCard, 1 ); #endif + + addShowData(); } /* Misc Page */ @@ -295,10 +405,11 @@ void MagRead::miscPage( bool partial ) { } tmpStr.replace( '|', "|" ); - tmpStr.prepend( "
" ); - tmpStr.append( "
" ); + tmpStr.prepend( "
" ); + tmpStr.append( "
" ); QLabel *label = new QLabel( tmpStr ); + label->setFont( font ); scroll->setWidget( label ); scroll->setWidgetResizable( true ); @@ -313,6 +424,8 @@ void MagRead::miscPage( bool partial ) { } mainLayout->insertWidget( 0, scroll, 1 ); #endif + + removeShowData(); } diff --git a/magread.h b/magread.h index 1972650..fba0b42 100644 --- a/magread.h +++ b/magread.h @@ -52,6 +52,8 @@ #include "accountcard.h" #include "aamvacard.h" +#include "settingspage.h" + class MagRead : public QMainWindow { Q_OBJECT @@ -68,6 +70,9 @@ class MagRead : public QMainWindow { bool captureAudio; bool partialRead; bool onMainPage; + QFont font; + + QSettings *settings; void mainPage(); void creditPage(); @@ -87,7 +92,12 @@ class MagRead : public QMainWindow { QAction *settingsAction; QAction *aboutAction; QAction *exitAction; + QAction *showDataAction; #endif + void addShowData(); + void removeShowData(); + + QMenu *fileMenu; enum mboxStatus { INFORMATION, @@ -101,6 +111,9 @@ class MagRead : public QMainWindow { void toggleRead(); void togglePartialRead( bool _partialRead ); void aboutDialogue(); + void showData(); + void settingsPage(); + void autoReorient( bool enabled ); }; #endif // MAGREAD_H diff --git a/main.cpp b/main.cpp index adef377..2aa8102 100644 --- a/main.cpp +++ b/main.cpp @@ -23,11 +23,12 @@ int main(int argc, char *argv[]) { QApplication a(argc, argv); - MagRead w; a.setApplicationName( "MagRead" ); a.setApplicationVersion( QUOTE( APP_VERSION ) ); a.setOrganizationDomain( "tehinterweb.com" ); a.setOrganizationName( "lint" ); + + MagRead w; #if defined(Q_WS_S60) w.showMaximized(); #else diff --git a/mslib.c b/mslib.c index 41bc62b..0cf41eb 100644 --- a/mslib.c +++ b/mslib.c @@ -164,7 +164,7 @@ void ms_peaks_filter_group( msData *ms ) { LList *trav; LListH *groupList; - int pos;//indicates pos/neg (not position) + int pos;/* indicates pos/neg (not position) */ if( !ms || ms->peakList->len < 2 ) return; @@ -345,7 +345,7 @@ int ms_decode_bits( msData *ms ) { validSwipe = 0; bitStream = strchr( ms->bitStream, '1' ); - if( bitStream == NULL ) // if stream contains no 1s, it's bad, just quit + if( bitStream == NULL ) /* if stream contains no 1s, it's bad, just quit */ return 1; bitStreamLen = strlen( bitStream ); @@ -357,7 +357,7 @@ int ms_decode_bits( msData *ms ) { curChar = _ms_decode_bits_char( bitStream + i, LRC, ms->dataType ); charStream[ len ] = curChar; if( curChar == BAD_CHAR ) - badChars++; // count the bad chars + badChars++; /* count the bad chars */ } charStream[ len ] = '\0'; @@ -394,8 +394,8 @@ int ms_decode_bits( msData *ms ) { char _ms_decode_bits_char( char *bitStream, char *LRC, ms_dataType type ) { int parity = 0, i; char out; - int len; // char length not including parity - int offset; // offset to make it ASCII + int len; /* char length not including parity */ + int offset; /* offset to make it ASCII */ if( type == ABA ) { len = ABA_CHAR_LEN - 1; @@ -406,16 +406,16 @@ char _ms_decode_bits_char( char *bitStream, char *LRC, ms_dataType type ) { } for( i = 0, out = 0; i < len; i++ ) { - out |= ( bitStream[ i ] - NUM_ASCII_OFFSET ) << i; // using OR to assign the bits into the char + out |= ( bitStream[ i ] - NUM_ASCII_OFFSET ) << i; /* using OR to assign the bits into the char */ if( bitStream[ i ] == '1' ) { - LRC[ i ] = !LRC[ i ]; // flip the bit in the LRC for all 1 bits in the char - parity++; // count the number of 1 bits for the parity bit + LRC[ i ] = !LRC[ i ]; /* flip the bit in the LRC for all 1 bits in the char */ + parity++; /* count the number of 1 bits for the parity bit */ } } out += offset; if( ( parity & 1 ) == ( bitStream[ len ] - NUM_ASCII_OFFSET ) ) - out = BAD_CHAR; // return the error char if the calculated parity bit doesn't match the recorded one + out = BAD_CHAR; /* return the error char if the calculated parity bit doesn't match the recorded one */ return out; } diff --git a/mslib.h b/mslib.h index 20c8ec8..65b5a9b 100644 --- a/mslib.h +++ b/mslib.h @@ -40,7 +40,7 @@ extern "C" { #define IATA_SS "1010001" #define IATA_ES "1111100" -#define MAX_IATA_LEN 83 // some reports say max len is 79, but AAMVA says 82 ... +#define MAX_IATA_LEN 83 /* some reports say max len is 79, but AAMVA says 82 ... */ #define IATA_CHAR_LEN 7 #define IATA_ASCII_OFFSET 32 @@ -132,7 +132,7 @@ const char *ms_get_bitStream( msData *ms ); const char *ms_get_charStream( msData *ms ); /* Reverses a string in place */ -void ms_strrev( char *str ); +void strrev( char *str ); diff --git a/settingspage.cpp b/settingspage.cpp new file mode 100644 index 0000000..98ad097 --- /dev/null +++ b/settingspage.cpp @@ -0,0 +1,262 @@ +/* + This file is part of MagRead. + + MagRead is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + MagRead is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with MagRead. If not, see . + + Written by Jeffrey Malone + http://blog.tehinterweb.com +*/ +#include "settingspage.h" +#include + +SettingsPage::SettingsPage() { + widget = new QWidget; + + layout = new QVBoxLayout; + widget->setLayout( layout ); + + settings = new QSettings; + if( !settings->contains( "formatCredit" ) ) + resetAll( false ); + + makeGeneralBox(); + makeAudioBox(); + + resetButton = new QPushButton( "Reset to defaults" ); + layout->addWidget( resetButton ); + connect( resetButton, SIGNAL( clicked() ), this, SLOT( resetAll() ) ); + + setWidget( widget ); + setWidgetResizable( true ); +} + +void SettingsPage::makeGeneralBox() { + generalBox = new QGroupBox( "General Settings" ); + + generalLayout = new QVBoxLayout; + + formatCredit = new QCheckBox( "Format Bank Cards" ); + generalLayout->addWidget( formatCredit ); + if( settings->value( "formatCredit" ) == true ) + formatCredit->setCheckState( Qt::Checked ); + connect( formatCredit, SIGNAL( stateChanged( int ) ), this, SLOT( formatCredit_checked( int ) ) ); + + formatAAMVA = new QCheckBox( "Format AAMVA Cards" ); + generalLayout->addWidget( formatAAMVA ); + if( settings->value( "formatAAMVA" ) == true ) + formatAAMVA->setCheckState( Qt::Checked ); + connect( formatAAMVA, SIGNAL( stateChanged( int ) ), this, SLOT( formatAAMVA_checked( int ) ) ); + +#ifdef Q_WS_MAEMO_5 + autoReorient = new QCheckBox( "Auto-reorient Screen" );//not symbian + generalLayout->addWidget( autoReorient ); + if( settings->value( "autoReorient" ) == true ) + autoReorient->setCheckState( Qt::Checked ); + connect( autoReorient, SIGNAL( stateChanged( int ) ), this, SLOT( autoReorient_checked( int ) ) ); +#endif + timeOutLabel = new QLabel( "Swipe Timeout: 10" ); + generalLayout->addWidget( timeOutLabel ); + + timeOutSlider = new QSlider( Qt::Horizontal ); + timeOutSlider->setTickInterval( 1 ); + timeOutSlider->setRange( 0, 100 ); + if( settings->contains( "timeOut" ) ) { + timeOutLabel->setText( QString( "Swipe Timeout: %1" ).arg( settings->value( "timeOut" ).toInt() ) ); + timeOutSlider->setSliderPosition( settings->value( "timeOut" ).toInt() ); + } else { + timeOutSlider->setSliderPosition( 10 ); + } + + connect( timeOutSlider, SIGNAL( valueChanged( int ) ), this, SLOT( timeOutChanged( int ) ) ); + + generalLayout->addWidget( timeOutSlider ); + + decodeMethodLabel = new QLabel( "Decoding Algorithm" ); + generalLayout->addWidget( decodeMethodLabel ); + + decodeMethodIntersect = new QRadioButton( "Instersection Algorithm", this ); + connect( decodeMethodIntersect, SIGNAL( toggled( bool ) ), this, SLOT( decodeMethodChanged( bool ) ) ); + generalLayout->addWidget( decodeMethodIntersect ); + + decodeMethodWalk = new QRadioButton( "Walking Algorithm", this ); + connect( decodeMethodWalk, SIGNAL( toggled( bool ) ), this, SLOT( decodeMethodChanged( bool ) ) ); + generalLayout->addWidget( decodeMethodWalk ); + + if( settings->value( "algorithm" ).toString() == "intersect" ) + decodeMethodIntersect->setChecked( true ); + else + decodeMethodWalk->setChecked( true ); + + generalBox->setLayout( generalLayout ); + + layout->addWidget( generalBox ); +} + +void SettingsPage::formatCredit_checked( int state ) { + if( state == Qt::Checked ) + settings->setValue( "formatCredit", true ); + else + settings->setValue( "formatCredit", false ); +} + +void SettingsPage::formatAAMVA_checked( int state ) { + if( state == Qt::Checked ) + settings->setValue( "formatAAMVA", true ); + else + settings->setValue( "formatAAMVA", false ); +} + +void SettingsPage::autoReorient_checked( int state ) { + if( state == Qt::Checked ) + settings->setValue( "autoReorient", true ); + else + settings->setValue( "autoReorient", false ); +#ifdef Q_WS_MAEMO_5 + if( settings->value( "autoReorient" ) == true ) { + emit autoReorientSig( true ); + } else { + emit autoReorientSig( false ); + } +#endif +} + +void SettingsPage::timeOutChanged( int value ) { + timeOutLabel->setText( QString( "Swipe Timeout: %1" ).arg( value ) ); + settings->setValue( "timeOut", value ); +} + +void SettingsPage::decodeMethodChanged( bool checked ) { + Q_UNUSED( checked ); + if( decodeMethodIntersect->isChecked() ) + settings->setValue( "algorithm", "intersect" ); + else + settings->setValue( "algorithm", "walk" ); +} + +void SettingsPage::makeAudioBox() { + audioBox = new QGroupBox( "Audio Settings" ); + + audioLayout = new QVBoxLayout; + + audioLayout->addWidget( new QLabel( "Audio Input:" ) ); + audioSource = new QComboBox; + QList inputDevices = QAudioDeviceInfo::availableDevices( QAudio::AudioInput ); + for( int i = 0; i < inputDevices.size(); i++ ) { + audioSource->addItem( inputDevices.at( i ).deviceName() ); + } + audioLayout->addWidget( audioSource ); + connect( audioSource, SIGNAL( currentIndexChanged( QString ) ), this, SLOT( audioDeviceChanged( QString ) ) ); + + normCBox = new QCheckBox( "Auto-Detect Normalzation" ); + audioLayout->addWidget( normCBox ); + connect( normCBox, SIGNAL( stateChanged( int ) ), this, SLOT( normChecked( int ) ) ); + + normLabel = new QLabel; + audioLayout->addWidget( normLabel ); + + normSlider = new QSlider( Qt::Horizontal ); + normSlider->setTickInterval( 10 ); + normSlider->setRange( -500, 500 ); + audioLayout->addWidget( normSlider ); + connect( normSlider, SIGNAL( valueChanged( int ) ), this, SLOT( normChanged( int ) ) ); + + if( settings->contains( "norm" ) ) + normSlider->setSliderPosition( settings->value( "norm" ).toInt() ); + else + normSlider->setSliderPosition( 0 ); + + if( !settings->contains( "normAuto" ) || settings->value( "normAuto" ) == true ) { + normCBox->setCheckState( Qt::Checked ); + normSlider->setEnabled( false ); + normLabel->setText( QString( "Normalization: Auto-Detect" ) ); + } else { + normLabel->setText( QString( "Normalization: %1" ).arg( normSlider->value() ) ); + } + + silenceLabel = new QLabel( "Silence Theshold: 300" ); + audioLayout->addWidget( silenceLabel ); + + silenceSlider = new QSlider( Qt::Horizontal ); + silenceSlider->setTickInterval( 20 ); + silenceSlider->setRange( 0, 1000 ); + silenceSlider->setSliderPosition( 300 ); + audioLayout->addWidget( silenceSlider ); + connect( silenceSlider, SIGNAL( valueChanged( int ) ), this, SLOT( silenceChanged( int ) ) ); + + if( settings->contains( "silenceThreshold" ) ) { + silenceSlider->setSliderPosition( settings->value( "silenceThreshold" ).toInt() ); + } else { + silenceSlider->setSliderPosition( 300 ); + } + + silenceLabel->setText( QString( "Silence Threshold: %1" ).arg( silenceSlider->value() ) ); + + audioBox->setLayout( audioLayout ); + + layout->addWidget( audioBox ); +} + +void SettingsPage::audioDeviceChanged( QString audioDevice ) { + settings->setValue( "audioDevice", audioDevice ); +} + +void SettingsPage::normChecked( int state ) { + if( state == Qt::Checked ) { + normSlider->setEnabled( false ); + normLabel->setText( "Normalization: Auto-Detect" ); + settings->setValue( "normAuto", true ); + } else { + normSlider->setEnabled( true ); + normLabel->setText( QString( "Normalization: %1" ).arg( normSlider->value() ) ); + settings->setValue( "normAuto", false ); + } +} + +void SettingsPage::silenceChanged( int value ) { + silenceLabel->setText( QString( "Silence Threshold: %1" ).arg( value ) ); + settings->setValue( "silenceThreshold", value ); +} + +void SettingsPage::normChanged( int value ) { + normLabel->setText( QString( "Normalization: %1" ).arg( value ) ); + settings->setValue( "norm", value ); +} + + + +void SettingsPage::resetAll( bool updateGui ) { + settings->setValue( "formatCredit", true ); + settings->setValue( "formatAAMVA", true ); + settings->setValue( "autoReorient", true ); + settings->setValue( "timeOut", 10 ); + + settings->setValue( "normAuto", true ); + settings->setValue( "norm", 0 ); + settings->setValue( "silenceThreshold", 300 ); + + if( updateGui ) { + formatCredit->setCheckState( Qt::Checked ); + formatAAMVA->setCheckState( Qt::Checked ); +#ifdef Q_WS_MAEMO5 + autoReorient->setCheckState( Qt::Checked ); +#endif + timeOutChanged( 10 ); + + normCBox->setCheckState( Qt::Checked ); + normSlider->setSliderPosition( 0 ); + normLabel->setText( "Normalization: Auto-Detect" ); + + silenceChanged( 300 ); + } +} diff --git a/settingspage.h b/settingspage.h new file mode 100644 index 0000000..ff87c0d --- /dev/null +++ b/settingspage.h @@ -0,0 +1,90 @@ +/* + This file is part of MagRead. + + MagRead is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + MagRead is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with MagRead. If not, see . + + Written by Jeffrey Malone + http://blog.tehinterweb.com +*/ +#ifndef SETTINGSPAGE_H +#define SETTINGSPAGE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class SettingsPage : public QScrollArea { + Q_OBJECT + public: + explicit SettingsPage(); + + private: + QSettings *settings; + + QWidget *widget; + QVBoxLayout *layout; + QPushButton *resetButton; + + /* General Settings */ + void makeGeneralBox(); + QGroupBox *generalBox; + QVBoxLayout *generalLayout; + QCheckBox *formatCredit; + QCheckBox *formatAAMVA; + QCheckBox *autoReorient; + QLabel *timeOutLabel; + QSlider *timeOutSlider; + QLabel *decodeMethodLabel; + QRadioButton *decodeMethodIntersect; + QRadioButton *decodeMethodWalk; + + /* Audio Box */ + void makeAudioBox(); + QGroupBox *audioBox; + QVBoxLayout *audioLayout; + QComboBox *audioSource; + QCheckBox *normCBox; + QLabel *normLabel; + QSlider *normSlider; + QLabel *silenceLabel; + QSlider *silenceSlider; + + signals: + void autoReorientSig( bool ); + + private slots: + void formatCredit_checked( int state ); + void formatAAMVA_checked( int state ); + void autoReorient_checked( int state ); + + void audioDeviceChanged( QString audioDevice ); + void timeOutChanged( int value ); + void decodeMethodChanged( bool checked ); + void normChanged( int value ); + void silenceChanged( int value ); + void normChecked( int state ); + + void resetAll( bool updateGui = true ); + +}; + +#endif // SETTINGSPAGE_H