HiPi
view release on metacpan or search on metacpan
lib/HiPi/Interface/MFRC522.pm view on Meta::CPAN
}
if ($i == 0) {
return ( MFRC522_STATUS_TIMEOUT, [] );
}
# Stop now if any errors except collisions were detected.
my $errorRegValue = $self->read_register(MFRC522_REG_ErrorReg); # // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr
if ($errorRegValue & 0x13) { #// BufferOvfl ParityErr ProtocolErr
return ( MFRC522_STATUS_ERROR, [] );
}
my $check_validBits = 0;
my @rdata = ();
# warn qq(something's out there .....);
# If the caller wants data back, get it from the MFRC522.
if ($getlen) {
# warn qq(let's read what it says .....);
my $haslen = $self->read_register(MFRC522_REG_FIFOLevelReg); #// Number of bytes in the FIFO
@rdata = $self->read_fifo($haslen);
$getlen = $haslen;
if ($rxalign) { #// Only update bit positions rxAlign..7 in values[0]
#// Create bit mask for bit positions rxAlign..7
my $mask = (0xFF << $rxalign) & 0xFF;
# // Apply mask to both current value of values[0] and the new data in value.
$rdata[0] = ( $rdata[0] & ~$mask) | ( $rdata[0] & $mask );
}
$check_validBits = $self->read_register(MFRC522_REG_ControlReg) & 0x07; #// RxLastBits[2:0] indicates the number of valid bits in the last received byte. If this value is 000b, the whole byte is valid.
$validbits = $check_validBits if $validbits;
}
# // Tell about collisions
if ($errorRegValue & 0x08) { # CollErr
return ( MFRC522_STATUS_COLLISION, \@rdata, $validbits );
}
# // Perform CRC_A validation if requested.
if ( @rdata && $checkcrc) {
# // In this case a MIFARE Classic NAK is not OK.
if ( $getlen == 1 && $check_validBits == 4) {
return ( MFRC522_STATUS_MIFARE_NACK , \@rdata, $validbits);
}
#// We need at least the CRC_A value and all 8 bits of the last byte must be received.
if ( $getlen < 2 || $check_validBits != 0) {
return ( MFRC522_STATUS_CRC_WRONG, \@rdata, $validbits );
}
#// Verify CRC_A - do our own calculation and store the control in controlBuffer.
my @crcdata = @rdata;
pop @crcdata; pop @crcdata;
my ( $crcstatus, $cbuffer1, $cbuffer2 ) = $self->pcd_calculate_crc( \@crcdata );
if ($crcstatus != MFRC522_STATUS_OK) {
return ($crcstatus, \@rdata, $check_validBits) ;
}
if (($rdata[-2] != $cbuffer1 ) || ($rdata[-1] != $cbuffer2)) {
return (MFRC522_STATUS_CRC_WRONG, \@rdata, $check_validBits) ;
}
}
return ( MFRC522_STATUS_OK, \@rdata, $check_validBits );
}
sub pcd_calculate_crc {
my($self, $dataref) = @_;
$self->write_register(MFRC522_REG_CommandReg, MFRC522_IDLE); #// Stop any active command.
$self->write_register(MFRC522_REG_DivIrqReg, 0x04); #// Clear the CRCIRq interrupt request bit
$self->write_register(MFRC522_REG_FIFOLevelReg, 0x80); #// FlushBuffer = 1, FIFO initialization
$self->write_register(MFRC522_REG_FIFODataReg, @$dataref); #// Write data to the FIFO
$self->write_register(MFRC522_REG_CommandReg, MFRC522_CALCCRC); #// Start the calculation
#// Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73?s.
#// TODO check/modify for other architectures than Arduino Uno 16bit
#// Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73us.
for (my $i = 5000; $i > 0; $i--) {
#// DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved
my $checkirq = $self->read_register(MFRC522_REG_DivIrqReg);
if ($checkirq & 0x04) { # // CRCIRq bit set - calculation done
$self->write_register(MFRC522_REG_CommandReg, MFRC522_IDLE); # // Stop calculating CRC for new content in the FIFO.
# // Transfer the result from the registers to the result buffer
my $r1 = $self->read_register(MFRC522_REG_CRCResultRegL);
my $r2 = $self->read_register(MFRC522_REG_CRCResultRegH);
return ( MFRC522_STATUS_OK, $r1, $r2 );
}
$self->sleep_microseconds( $self->scanwait );
}
return ( MFRC522_STATUS_TIMEOUT, undef, undef );
}
sub picc_is_new_tag_present {
my $self = shift;
#// Reset baud rates
$self->write_register(MFRC522_REG_TxModeReg, 0x00);
$self->write_register(MFRC522_REG_RxModeReg, 0x00);
#// Reset ModWidthReg
$self->write_register(MFRC522_REG_ModWidthReg, 0x26);
my( $status, $data, $validbits ) = $self->picc_request_active();
my $result = ( $status == MFRC522_STATUS_OK || $status == MFRC522_STATUS_COLLISION );
return $result;
}
sub picc_request_active {
my($self) = @_;
my( $status, $data, $validbits ) = $self->picc_request_idl_or_wup( MIFARE_REQIDL, 2 );
return ( $status, $data, $validbits );
lib/HiPi/Interface/MFRC522.pm view on Meta::CPAN
my($self, $command, $getlen) = @_;
# command is MIFARE_REQIDL or MIFARE_REQALL
$self->clear_bit_mask(MFRC522_REG_CollReg, 0x80); #// ValuesAfterColl=1 => Bits received after collision are cleared.
my $validbits = 7; #// For REQA and WUPA we need the short frame format - transmit only 7 bits of the last (and only) byte. TxLastBits = BitFramingReg[2..0]
my($status, $data);
( $status, $data, $validbits ) = $self->pcd_transceive_data( $command , $validbits, $getlen );
if ($status != MFRC522_STATUS_OK) {
return ( $status, undef, undef );
}
if (scalar @$data != 2 || $validbits != 0) { #// ATQA must be exactly 16 bits.
return ( MFRC522_STATUS_ERROR, undef, undef );
}
return ( $status, $data, $validbits );
}
sub pcd_transceive_data {
my($self, $senddata, $validbitsin, $getlen, $rxalign, $checkcrc ) = @_;
my $sendref = ( ref($senddata) eq 'ARRAY') ? $senddata : [ $senddata ];
my $waitirq = 0x30;
# // we sometimes pass in null bytes
for (my $i = 0; $i < @$sendref; $i ++ ) {
$sendref->[$i] //= 0;
}
my ($status, $data, $validbits) = $self->pcd_communicate_with_picc(
MFRC522_TRANSCEIVE, $waitirq, $sendref, $getlen, $validbitsin, $rxalign, $checkcrc
);
return ($status, $data, $validbits);
}
sub pcd_mifare_transceive {
my($self, $data, $accepttimeout) = @_;
$accepttimeout = ( $accepttimeout ) ? 1 : 0;
#// Copy sendData[] to cmdBuffer[] and add CRC_A
my ( $crcstatus, $cbuffer1, $cbuffer2 ) = $self->pcd_calculate_crc( $data );
if ($crcstatus != MFRC522_STATUS_OK) {
return ( $crcstatus, undef );
}
my @sendbuffer = @$data;
push( @sendbuffer, $cbuffer1, $cbuffer2 );
#// Transceive the data, store the reply in cmdBuffer[]
my $waitIRq = 0x30; #// RxIRq and IdleIRq
my $validBits = 0;
my $getlen = 16;
my ($status, $piccdata, $validbitsout) = $self->pcd_communicate_with_picc(
MFRC522_TRANSCEIVE, $waitIRq, \@sendbuffer, $getlen, $validBits
);
if ($accepttimeout && $status == MFRC522_STATUS_TIMEOUT) {
return (MFRC522_STATUS_OK, [] );
}
if ($status != MFRC522_STATUS_OK) {
return ( $status, undef );
}
#// The PICC must reply with a 4 bit ACK
my $returnbuffersize = scalar @$piccdata;
if ($returnbuffersize != 1 || $validbitsout != 4) {
return ( MFRC522_STATUS_ERROR, undef );
}
if ($piccdata->[0] != MIFARE_MF_ACK) {
return ( MFRC522_STATUS_MIFARE_NACK, undef );
}
return ($status, $piccdata, $validbitsout);
}
sub picc_read_tag_serial {
my $self = shift;
my( $status, $uid ) = $self->picc_select;
my $serialstring = '';
if( $status == MFRC522_STATUS_OK ) {
for (my $i = 0; $i < $uid->{'size'}; $i ++ ) {
$serialstring .= '-' if $serialstring;
$serialstring .= sprintf('%02X', $uid->{'data'}->[$i]);
}
}
return ( $status, $uid, $serialstring );
}
sub picc_select {
my ($self, $uid, $validbits ) = @_;
$uid //= {
size => 0,
data => [],
sak => 0,
};
$validbits ||= 0;
# bool uidComplete;
# bool selectDone;
# bool useCascadeTag;
# byte cascadeLevel = 1;
# MFRC522::StatusCode result;
# byte count;
# byte checkBit;
# byte index;
# byte uidIndex; // The first index in uid->uidByte[] that is used in the current Cascade Level.
# int8_t currentLevelKnownBits; // The number of known UID bits in the current Cascade Level.
# byte buffer[9]; // The SELECT/ANTICOLLISION commands uses a 7 byte standard frame + 2 bytes CRC_A
# byte bufferUsed; // The number of bytes used in the buffer, ie the number of bytes to transfer to the FIFO.
# byte rxAlign; // Used in BitFramingReg. Defines the bit position for the first bit received.
# byte txLastBits; // Used in BitFramingReg. The number of valid bits in the last transmitted byte.
# byte *responseBuffer;
# byte responseLength;
#
# // Description of buffer structure:
# // Byte 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3
# // Byte 1: NVB Number of Valid Bits (in complete command, not just the UID): High nibble: complete bytes, Low nibble: Extra bits.
# // Byte 2: UID-data or CT See explanation below. CT means Cascade Tag.
# // Byte 3: UID-data
# // Byte 4: UID-data
# // Byte 5: UID-data
# // Byte 6: BCC Block Check Character - XOR of bytes 2-5
# // Byte 7: CRC_A
# // Byte 8: CRC_A
# // The BCC and CRC_A are only transmitted if we know all the UID bits of the current Cascade Level.
# //
# // Description of bytes 2-5: (Section 6.5.4 of the ISO/IEC 14443-3 draft: UID contents and cascade levels)
# // UID size Cascade level Byte2 Byte3 Byte4 Byte5
lib/HiPi/Interface/MFRC522.pm view on Meta::CPAN
$uidIndex = 3,
$useCascadeTag = ( $validbits && $uid->{'size'} > 7 ) ? 1 : 0;
} elsif( $cascadeLevel == 3 ) {
$buffer[0] = MIFARE_SELECT_CL3;
$uidIndex = 6,
$useCascadeTag = 0
} else {
# should not get here
# warn qq( cascade level $cascadeLevel);
return ( MFRC522_STATUS_INTERNAL_ERROR );
}
# // How many UID bits are known in this Cascade Level?
$currentLevelKnownBits = $validbits - (8 * $uidIndex);
if ($currentLevelKnownBits < 0) {
$currentLevelKnownBits = 0;
}
# // Copy the known bits from uid->uidByte[] to buffer[]
$index = 2; #// destination index in buffer[]
if ($useCascadeTag) {
$buffer[$index++] = MIFARE_CASCADE;
}
my $bytesToCopy = int($currentLevelKnownBits / 8) + ($currentLevelKnownBits % 8 ? 1 : 0); # // The number of bytes needed to represent the known bits for this level.
if ($bytesToCopy) {
my $maxBytes = $useCascadeTag ? 3 : 4; #// Max 4 bytes in each Cascade Level. Only 3 left if we use the Cascade Tag
if ($bytesToCopy > $maxBytes) {
$bytesToCopy = $maxBytes;
}
for (my $count = 0; $count < $bytesToCopy; $count++) {
$buffer[$index++] = $uid->{'data'}->[$uidIndex + $count] || 0;
}
}
# // Now that the data has been copied we need to include the 8 bits in CT in currentLevelKnownBits
if ($useCascadeTag) {
$currentLevelKnownBits += 8;
}
# // Repeat anti collision loop until we can transmit all UID bits + BCC and receive a SAK - max 32 iterations.
while (!$selectDone) {
# // Find out how many bits and bytes to send and receive.
if ($currentLevelKnownBits >= 32) { # // All UID bits in this Cascade Level are known. This is a SELECT.
$buffer[1] = 0x70; #// NVB - Number of Valid Bits: Seven whole bytes
#// Calculate BCC - Block Check Character
for( 2,3,4,5) {
$buffer[$_] //= 0;
}
$buffer[6] = $buffer[2] ^ $buffer[3] ^ $buffer[4] ^ $buffer[5];
# // Calculate CRC_A
my @crcdata = @buffer[0..6];
( $crcstatus, $cbuffer1, $cbuffer2 ) = $self->pcd_calculate_crc( \@crcdata );
if ($crcstatus != MFRC522_STATUS_OK) {
return ( $crcstatus, undef, undef );
}
# set the crc result
$buffer[7] = $cbuffer1;
$buffer[8] = $cbuffer2;
$txLastBits = 0; #// 0 => All 8 bits are valid.
$bufferUsed = 9;
$responseLength = 3;
$responseIndex = 6;
} else { #// This is an ANTICOLLISION.
$txLastBits = $currentLevelKnownBits % 8;
my $count = int($currentLevelKnownBits / 8); #// Number of whole bytes in the UID part.
$index = 2 + $count; #// Number of whole bytes: SEL + NVB + UIDs
$buffer[1] = ($index << 4) + $txLastBits; #// NVB - Number of Valid Bits
$bufferUsed = $index + ($txLastBits ? 1 : 0);
$responseLength = 9 - $index;
$responseIndex = $index;
}
#// Set bit adjustments
$rxAlign = $txLastBits || 0; #// Having a separate variable is overkill. But it makes the next line easier to read.
$self->write_register(MFRC522_REG_BitFramingReg, ($rxAlign << 4) + $txLastBits); #// RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0]
#// Transmit the buffer and receive the response.
my $getlen = $responseLength;
my $sendlen = $bufferUsed -1;
my @sendbuffer = @buffer[0..$sendlen];
($respstatus, $respdata, $respvalidbits) = $self->pcd_transceive_data(\@sendbuffer, $txLastBits, $getlen, $rxAlign );
$txLastBits = $respvalidbits;
if($respdata && ref($respdata)) {
for (my $i = 0; $i < @$respdata; $i ++) {
last if $i > $responseLength;
$buffer[ $i + $responseIndex ] = $respdata->[$i];
}
}
if ($respstatus == MFRC522_STATUS_COLLISION) { #// More than one PICC in the field => collision.
my $valueOfCollReg = $self->read_register(MFRC522_REG_CollReg); #// CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0]
if ($valueOfCollReg & 0x20) { #// CollPosNotValid
return ( MFRC522_STATUS_COLLISION, undef, undef ); # // Without a valid collision position we cannot continue
}
my $collisionPos = $valueOfCollReg & 0x1F; # // Values 0-31, 0 means bit 32.
if ($collisionPos == 0) {
$collisionPos = 32;
}
if ($collisionPos <= $currentLevelKnownBits) { #// No progress - should not happen
return ( MFRC522_STATUS_INTERNAL_ERROR, undef, undef );
}
#// Choose the PICC with the bit set.
$currentLevelKnownBits = $collisionPos;
my $count = $currentLevelKnownBits % 8; #// The bit to modify
my $checkBit = ($currentLevelKnownBits - 1) % 8;
$index = 1 + int($currentLevelKnownBits / 8) + ($count ? 1 : 0); # // First byte is index 0.
$buffer[$index] |= (1 << $checkBit);
} elsif ($respstatus != MFRC522_STATUS_OK) {
return ( $respstatus, undef, undef );
} else { # // MFRC522_STATUS_OK
if ($currentLevelKnownBits >= 32) { #// This was a SELECT.
$selectDone = 1; #// No more anticollision
#// We continue below outside the while.
} else { #// This was an ANTICOLLISION.
#/ We now have all 32 bits of the UID in this Cascade Level
$currentLevelKnownBits = 32;
#// Run loop again to do the SELECT.
}
}
}
#// We do not check the CBB - it was constructed by us above.
#// Copy the found UID bytes from buffer[] to uid->uidByte[]
$index = ($buffer[2] == MIFARE_CASCADE) ? 3 : 2; #// source index in buffer[]
$bytesToCopy = ($buffer[2] == MIFARE_CASCADE) ? 3 : 4;
for (my $count = 0; $count < $bytesToCopy; $count++) {
$uid->{'data'}->[$uidIndex + $count] = $buffer[$index++];
}
#// Check response SAK (Select Acknowledge)
my $resplen = scalar @$respdata;
if ($resplen != 3 || $respvalidbits != 0) { # // SAK must be exactly 24 bits (1 byte + CRC_A).
return ( MFRC522_STATUS_ERROR, undef, undef );
}
#// Verify CRC_A - do our own calculation and store the control in buffer[2..3] - those bytes are not needed anymore.
my @crcdata = @$respdata;
pop @crcdata; pop @crcdata;
( $crcstatus, $cbuffer1, $cbuffer2 ) = $self->pcd_calculate_crc( \@crcdata );
if ($crcstatus != MFRC522_STATUS_OK) {
return ( $crcstatus, undef, undef );
}
if (($cbuffer1 != $respdata->[-2]) || ($cbuffer2 != $respdata->[-1])) {
return ( MFRC522_STATUS_CRC_WRONG, undef, undef );
}
if ($respdata->[0] & 0x04) { #// Cascade bit set - UID not complete yes
$cascadeLevel++;
} else {
$uidComplete = 1;
$uid->{'sak'} = $respdata->[0];
}
}
#// Set correct uid->size
$uid->{'size'} = 3 * $cascadeLevel + 1;
return ( MFRC522_STATUS_OK, $uid, $respvalidbits );
}
sub get_status_code_name {
my($self, $code) = @_;
$code //= 0xEE;
if($code == MFRC522_STATUS_OK ) {
return 'Success.';
} elsif($code == MFRC522_STATUS_ERROR ) {
return 'Error in communication.';
} elsif($code == MFRC522_STATUS_COLLISION ) {
return 'Collision detected.';
} elsif($code == MFRC522_STATUS_TIMEOUT ) {
return 'Timeout in communication.';
} elsif($code == MFRC522_STATUS_NO_ROOM ) {
return 'A buffer is not big enough.';
} elsif($code == MFRC522_STATUS_INTERNAL_ERROR ) {
return 'Internal error in the code. Should not happen.';
} elsif($code == MFRC522_STATUS_INVALID ) {
return 'Invalid argument.';
} elsif($code == MFRC522_STATUS_CRC_WRONG ) {
return 'The CRC_A does not match.';
} elsif($code == MFRC522_STATUS_MIFARE_NACK ) {
return 'A MIFARE PICC responded with NAK.';
} elsif($code == MFRC522_STATUS_UNSUPPORTED_TYPE ) {
return 'Unsupported command for this PICC type.';
} elsif($code == MFRC522_STATUS_BLOCK_NOT_ALLOWED ) {
return 'Command not allowed for this block.';
} elsif($code == MFRC522_STATUS_BAD_PARAM ) {
return 'Bad parameter.';
} else {
return 'Unknown Error.';
}
}
sub get_default_key {
my $self = shift;
my @key = (0xFF) x 6;
return \@key;
}
lib/HiPi/Interface/MFRC522.pm view on Meta::CPAN
}
return $output . qq(\n);
}
sub picc_dump_ultralight_memory {
my($self, $uid, $picctype, $key) = @_;
my $piccname = $self->picc_get_type_name( $picctype );
my $output = qq(Dumping memory contents not implemented for $piccname\n);
return $output;
}
sub picc_dump_classic_memory {
my($self, $uid, $picctype, $key) = @_;
my $output = '';
my $no_of_sectors = 0;
if( $picctype == MFRC522_PICC_TYPE_MIFARE_MINI ) {
$no_of_sectors = 5;
} elsif( $picctype == MFRC522_PICC_TYPE_MIFARE_1K ) {
$no_of_sectors = 16;
} elsif( $picctype == MFRC522_PICC_TYPE_MIFARE_4K ) {
$no_of_sectors = 40;
}
#// Dump sectors, highest address first.
if ($no_of_sectors) {
$output .= qq(Sector Block 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 AccessBits\n);
for (my $i = $no_of_sectors -1; $i >= 0; $i-- ) {
$output .= $self->picc_dump_classic_sector( $uid, $key, $i );
}
}
my $haltstatus = $self->picc_halt_active;
unless($haltstatus == MFRC522_STATUS_OK) {
$output .= $self->get_status_code_name( $haltstatus ) . qq(\n);
}
$self->pcd_stop_crypto1;
return $output;
}
sub pcd_authenticate {
my($self, $command, $blockAddr, $key, $uid ) = @_;
my $waitIRq = 0x10;
my @sendData = ( $command, $blockAddr );
for (my $i = 0; $i < 6; $i++) { # // 6 key bytes
$sendData[2 + $i] = $key->[$i];
}
#// Use the last uid bytes as specified in http://cache.nxp.com/documents/application_note/AN10927.pdf
#// section 3.2.5 "MIFARE Classic Authentication".
#// The only missed case is the MF1Sxxxx shortcut activation,
#// but it requires cascade tag (CT) byte, that is not part of uid.
for (my $i = 0; $i < 4; $i++) { #// The last 4 bytes of the UID
$sendData[8 + $i] = $uid->{'data'}->[$i + $uid->{'size'} -4];
}
my ($piccstatus, $piccdata, $piccvalidbits) = $self->pcd_communicate_with_picc( MFRC522_AUTHENT, $waitIRq, \@sendData );
return $piccstatus;
}
sub picc_dump_classic_sector {
my($self, $uid, $key, $sector) = @_;
my $output = '';
my ( $status, $firstBlock, $no_of_blocks, $isSectorTrailer );
#byte firstBlock; // Address of lowest address to dump actually last block dumped)
#byte no_of_blocks; // Number of blocks in sector
#bool isSectorTrailer; // Set to true while handling the "last" (ie highest address) in the sector.
#// The access bits are stored in a peculiar fashion.
#// There are four groups:
#// g[3] Access bits for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39)
#// g[2] Access bits for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39)
#// g[1] Access bits for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39)
#// g[0] Access bits for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39)
#// Each group has access bits [C1 C2 C3]. In this code C1 is MSB and C3 is LSB.
#// The four CX bits are stored together in a nible cx and an inverted nible cx_.
#byte c1, c2, c3; // Nibbles
#byte c1_, c2_, c3_; // Inverted nibbles
my ( $c1, $c2, $c3, $c1x, $c2x, $c3x );
#bool invertedError; // True if one of the inverted nibbles did not match
#byte g[4]; // Access bits for each of the four groups.
#byte group; // 0-3 - active group for access bits
#bool firstInGroup; // True for the first block dumped in the group
my( $invertedError, $group, $firstInGroup );
my @g = (0,0,0,0);
#// Determine position and size of sector.
if ($sector < 32) { #// Sectors 0..31 has 4 blocks each
$no_of_blocks = 4;
$firstBlock = $sector * $no_of_blocks;
} elsif ($sector < 40) { #// Sectors 32-39 has 16 blocks each
$no_of_blocks = 16;
$firstBlock = 128 + ($sector - 32) * $no_of_blocks;
} else { #// Illegal input, no MIFARE Classic PICC has more than 40 sectors.
return qq(Illegal input, no MIFARE Classic PICC has more than 40 sectors\n);
}
#// Dump blocks, highest address first.
my $byteCount;
my @buffer = ( 0 ) x 18;
my $blockAddr;
$isSectorTrailer = 1;
$invertedError = 0; # // Avoid "unused variable" warning.
for (my $blockOffset = $no_of_blocks - 1; $blockOffset >= 0; $blockOffset-- ) {
$blockAddr = $firstBlock + $blockOffset;
#// Sector number - only on first line
if ($isSectorTrailer) {
if($sector < 10) {
$output .= ' '; #// Pad with spaces
} else {
$output .= ' '; #// Pad with spaces
}
( run in 0.572 second using v1.01-cache-2.11-cpan-97f6503c9c8 )