aboutsummaryrefslogtreecommitdiff
path: root/jindroush/lib/cdsk_dcm.cpp
diff options
context:
space:
mode:
authorB. Watson <urchlay@slackware.uk>2024-05-16 01:43:09 -0400
committerB. Watson <urchlay@slackware.uk>2024-05-16 01:43:09 -0400
commita4cc3ad3504d634e379369862c9f9fd8eed379f3 (patch)
tree7b6f55c352a4ca62dddaa1b4a6854799111d2d2f /jindroush/lib/cdsk_dcm.cpp
parentb33c25d1363110e6e4a714530f460b0ff951f56b (diff)
downloadbw-atari8-tools-a4cc3ad3504d634e379369862c9f9fd8eed379f3.tar.gz
Add Jindrich Kubec's tools.
Diffstat (limited to 'jindroush/lib/cdsk_dcm.cpp')
-rw-r--r--jindroush/lib/cdsk_dcm.cpp762
1 files changed, 762 insertions, 0 deletions
diff --git a/jindroush/lib/cdsk_dcm.cpp b/jindroush/lib/cdsk_dcm.cpp
new file mode 100644
index 0000000..06ec1a3
--- /dev/null
+++ b/jindroush/lib/cdsk_dcm.cpp
@@ -0,0 +1,762 @@
+// This program 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 2 of the License, or
+// any later version.
+//
+// This program 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 this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "cdsk_dcm.h"
+#include "cfile.h"
+#include "cprefile.h"
+#include "autil.h"
+
+//#define _DCM_DUMP_
+
+#define DCM_CHANGE_BEGIN 0x41 //Change only start of sector
+#define DCM_DOS_SECTOR 0x42 //128 byte compressed sector
+#define DCM_COMPRESSED 0x43 //Uncompressed/compressed pairs
+#define DCM_CHANGE_END 0x44 //Change only end of sector
+#define DCM_PASS_END 0x45 //End of pass
+#define DCM_SAME_AS_BEFORE 0x46 //Same as previous non-zero
+#define DCM_UNCOMPRESSED 0x47 //Uncompressed sector
+
+#define DCM_HEADER_SINGLE 0xFA
+#define DCM_HEADER_MULTI 0xF9
+
+#define DCM_DENSITY_SD 0 //Single density, 90K
+#define DCM_DENSITY_DD 1 //Double density, 180K
+#define DCM_DENSITY_ED 2 //Enhanced density, 130K
+
+CDcm::CDcm() : CDisk()
+{
+ #ifdef _MEMORY_DUMP_
+ printf( "CDcm constructed: %08X\n", this );
+ #endif
+}
+
+CDcm::~CDcm()
+{
+ #ifdef _MEMORY_DUMP_
+ printf( "CDcm destructed: %08X\n", this );
+ #endif
+}
+
+BOOL CDcm::Load( char* szFname, BOOL, BOOL )
+{
+ BYTE btArcType = 0; //Block type for first block
+ BYTE btBlkType; //Current block type
+
+ m_bAlreadyFormatted = FALSE;
+ m_bLastPass = FALSE;
+ m_wCurrentSector = 0;
+
+ CFile cfo;
+ CPreFile cf;
+
+ if ( !cfo.Open( szFname ) )
+ {
+ sprintf( m_szLastError, "DCM: Can't open '%s'", szFname );
+ m_iErrorCode = CDISK_ERROR_CANT_OPEN;
+ return FALSE;
+ }
+
+ strcpy( m_szFname, szFname );
+
+ cf.Open( &cfo, 20 );
+
+ m_lFileLength = cf.GetLength();
+
+ for(;;) //outpass
+ {
+ if ( cf.Tell() >= m_lFileLength )
+ {
+ if ( ( !m_bLastPass ) && ( btArcType == DCM_HEADER_MULTI ) )
+ {
+ sprintf( m_szLastError,"DCM: Multi-part archive error.\n" \
+ "To process these files, you must first combine the files into a single file." );
+ m_iErrorCode = CDCM_FORMAT_VIOLATED;
+ return FALSE;
+ }
+ }
+
+ btArcType = cf.readb();
+
+ switch( btArcType )
+ {
+ case DCM_HEADER_MULTI:
+ case DCM_HEADER_SINGLE:
+ if ( !DecodeRecFA( cf ) )
+ return FALSE;
+ break;
+
+ default:
+ sprintf( m_szLastError, "DCM: %02X is an unknown header block.\n", btArcType );
+ return FALSE;
+ }
+
+ for(;;) //inpass
+ {
+ btBlkType = cf.readb();
+
+ if ( btBlkType == DCM_PASS_END )
+ break;
+
+ if ( cf.Tell() >= m_lFileLength )
+ {
+ sprintf( m_szLastError, "DCM: EOF before end block." );
+ m_iErrorCode = CDCM_FORMAT_VIOLATED;
+ return FALSE;
+ }
+
+ BOOL bRes = TRUE;
+
+ *m_szLastError = '\0';
+ switch( btBlkType & 0x7F )
+ {
+ case DCM_CHANGE_BEGIN:
+ bRes = DecodeRec41( cf );
+ break;
+
+ case DCM_DOS_SECTOR:
+ bRes = DecodeRec42( cf );
+ break;
+
+ case DCM_COMPRESSED:
+ bRes = DecodeRec43( cf );
+ break;
+
+ case DCM_CHANGE_END:
+ bRes = DecodeRec44( cf );
+ break;
+
+ case DCM_SAME_AS_BEFORE:
+ //not needed
+ //bRes = DecodeRec46( cf );
+ break;
+
+ case DCM_UNCOMPRESSED:
+ bRes = DecodeRec47( cf );
+ break;
+
+ default:
+ {
+ switch( btBlkType )
+ {
+ case DCM_HEADER_MULTI:
+ case DCM_HEADER_SINGLE:
+ sprintf( m_szLastError, "DCM: Trying to start section but last section never had "
+ "an end section block.");
+ break;
+
+ default:
+ sprintf( m_szLastError, "DCM: %02X is an unknown block type. File may be "
+ "corrupt.",btBlkType);
+ break;
+ }
+
+ m_iErrorCode = CDCM_FORMAT_VIOLATED;
+ return FALSE;
+ }
+ }
+
+ if ( !bRes )
+ {
+ sprintf( m_szLastError, "DCM: Block %02X decode error!", btBlkType );
+ m_iErrorCode = CDCM_FORMAT_VIOLATED;
+ return FALSE;
+ }
+
+ if ( !WriteSector( m_wCurrentSector, m_abtCurrBuff ) )
+ return FALSE;
+
+ if ( btBlkType & 0x80 )
+ m_wCurrentSector++;
+ else
+ m_wCurrentSector = cf.readLEw();
+
+ } //infinite for (inpass)
+
+ //End block
+ if ( m_bLastPass )
+ break;
+
+ } //infinite for (outpass)
+
+ cf.Close();
+ cfo.Close();
+ return TRUE;
+
+}
+
+BOOL CDcm::DecodeRec41( CGenFile& cf )
+{
+ #ifdef _DCM_DUMP_
+ printf( "dec41: %08lX\n", cf.Tell() - 1 );
+ #endif
+
+ int iOffset = cf.readb();
+ BYTE* pbt = m_abtCurrBuff + iOffset;
+
+ do
+ {
+ *( pbt-- ) = cf.readb();
+ } while( iOffset-- );
+
+ return TRUE;
+}
+
+BOOL CDcm::DecodeRec42( CGenFile& cf )
+{
+ #ifdef _DCM_DUMP_
+ printf( "dec42: %08lX\n", cf.Tell() - 1 );
+ #endif
+
+ sprintf( m_szLastError, "DCM: Record type 0x42 untested. Uncomment?" );
+ return FALSE;
+
+ //TODO: uncomment later!
+ //cf.Read( m_abtCurrBuff + 123, 5 );
+ //memset( m_abtCurrBuff, m_abtCurrBuff[ 123 ], 123 );
+ //return TRUE;
+}
+
+BOOL CDcm::DecodeRec43( CGenFile& cf )
+{
+ #ifdef _DCM_DUMP_
+ printf( "dec43: %08lX\n", cf.Tell() - 1 );
+ #endif
+
+ BYTE* pbtP = m_abtCurrBuff;
+ BYTE* pbtE;
+
+ BYTE* pbtEnd = m_abtCurrBuff + m_iSectorSize;
+
+ do
+ {
+ //uncompressed string
+ if ( pbtP != m_abtCurrBuff )
+ pbtE = m_abtCurrBuff + ReadOffset( cf );
+ else
+ pbtE = m_abtCurrBuff + cf.readb();
+
+ if ( pbtE < pbtP )
+ return FALSE;
+
+ #ifdef _DCM_DUMP_
+ printf( "dec43: uncst: %p %p %ld\n", pbtP, pbtE, pbtE - pbtP );
+ #endif
+
+ if ( pbtE != pbtP )
+ {
+ cf.Read( pbtP, pbtE - pbtP );
+ pbtP = pbtE;
+ }
+
+ if ( pbtP >= pbtEnd )
+ break;
+
+ //rle compressed string
+ pbtE = m_abtCurrBuff + ReadOffset( cf );
+ BYTE c = cf.readb();
+
+ #ifdef _DCM_DUMP_
+ printf( "dec43: cst: %p %p %ld\n", pbtP, pbtE, pbtE - pbtP );
+ #endif
+
+ if ( pbtE < pbtP )
+ return FALSE;
+
+ memset( pbtP, c, pbtE - pbtP );
+ pbtP = pbtE;
+
+ } while( pbtP < pbtEnd );
+
+ return TRUE;
+}
+
+BOOL CDcm::DecodeRec44( CGenFile& cf )
+{
+ #ifdef _DCM_DUMP_
+ printf( "dec44: %08lX\n", cf.Tell() - 1 );
+ #endif
+
+ int iOffset = ReadOffset( cf );
+
+ cf.Read( m_abtCurrBuff + iOffset, m_iSectorSize - iOffset );
+
+ return TRUE;
+}
+
+BOOL CDcm::DecodeRec46( CGenFile& cf )
+{
+ #ifdef _DCM_DUMP_
+ printf( "dec46: %08lX\n", cf.Tell() - 1 );
+ #endif
+
+ return TRUE;
+}
+
+BOOL CDcm::DecodeRec47( CGenFile& cf )
+{
+ #ifdef _DCM_DUMP_
+ printf( "dec47: %08lX\n", cf.Tell() - 1 );
+ #endif
+
+ //TODO: Is this TRUE or NOT???
+ //cf.Read( m_abtCurrBuff, ( m_wCurrentSector < 4 ? 128 : m_iSectorSize ) );
+
+ cf.Read( m_abtCurrBuff, m_iSectorSize );
+ return TRUE;
+}
+
+BOOL CDcm::DecodeRecFA( CGenFile& cf )
+{
+ #ifdef _DCM_DUMP_
+ printf( "decFA: %08lX\n", cf.Tell() - 1 );
+ #endif
+
+ BYTE btPom = cf.readb();
+
+ BYTE btDensity = ( btPom >> 5 ) & 0x03;
+ //BYTE btPass = btPom & 0x1F;
+ m_bLastPass = ( btPom & 0x80 ) ? TRUE : FALSE;
+
+ int iSpT;
+ int iTracks;
+
+ switch( btDensity )
+ {
+ case DCM_DENSITY_SD:
+ iTracks = 40;
+ iSpT = 18;
+ m_iSectorSize = 128;
+ break;
+
+ case DCM_DENSITY_DD:
+ iTracks = 40;
+ iSpT = 18;
+ m_iSectorSize = 256;
+ break;
+
+ case DCM_DENSITY_ED:
+ iTracks = 40;
+ iSpT = 26;
+ m_iSectorSize = 128;
+ break;
+
+ default:
+ sprintf( m_szLastError,"DCM: Density type unknown (%02X)\n", btDensity );
+ return FALSE;
+ }
+
+ if ( !m_bAlreadyFormatted )
+ {
+ DISK_GEOMETRY dg;
+ dg.iSides = 1;
+ dg.iTracks = iTracks;
+ dg.iSectorsPerTrack = iSpT;
+ dg.iBytesPerSector = m_iSectorSize;
+
+ if ( !Format( &dg ) )
+ return FALSE;
+
+ m_bAlreadyFormatted = TRUE;
+ }
+
+ m_wCurrentSector = cf.readLEw();
+
+ return TRUE;
+}
+
+WORD CDcm::ReadOffset( CGenFile& cf )
+{
+ BYTE bt = cf.readb();
+
+ return( bt ? bt : 256 );
+}
+
+#ifdef __CDISK_WRITE__
+
+BOOL CDcm::Save( char* szOutFile, BOOL bOverWrite )
+{
+ if ( !bOverWrite && !access( szOutFile, F_OK ) )
+ {
+ sprintf( m_szLastError, "DCM: File already exists! '%s'", szOutFile );
+ return FALSE;
+ }
+
+ int iDensity = -1;
+ m_iSectorSize = m_geometry.iBytesPerSector;
+
+ if ( m_iSectorSize == 0x80 )
+ {
+ if ( m_geometry.iTracks == 40 )
+ {
+ if ( m_geometry.iSectorsPerTrack == 18 )
+ iDensity = DCM_DENSITY_SD;
+
+ if ( m_geometry.iSectorsPerTrack == 26 )
+ iDensity = DCM_DENSITY_ED;
+ }
+ }
+
+ if ( m_iSectorSize == 0x100 )
+ {
+ if ( ( m_geometry.iSectorsPerTrack == 18 ) &&
+ ( m_geometry.iTracks == 40 ) )
+ iDensity = DCM_DENSITY_DD;
+ }
+
+ if ( iDensity == - 1 )
+ {
+ sprintf( m_szLastError, "DCM: Can't work with such density!" );
+ return FALSE;
+ }
+
+ int iPass = 1;
+
+ m_pbtPass = new BYTE [ 0x6500 ];
+
+ CFile cf;
+
+ if ( !cf.Create( szOutFile ) )
+ {
+ sprintf( m_szLastError, "DCM: Can't create '%s'", szOutFile );
+ delete [] m_pbtPass;
+ return FALSE;
+ }
+
+ int iFirstSector = 0;
+ int iPrevSector = 0;
+ int iCurrentSector = 1;
+
+ memset( m_abtPrevBuff, 0, m_iSectorSize );
+
+ EncodeRecFA( FALSE, iPass, iDensity, iFirstSector );
+
+ //here should be other compression
+
+ while( iCurrentSector <= m_geometry.iSectors )
+ {
+ iFirstSector = 0;
+
+ while( ( m_pbtCurr - m_pbtPass ) < 0x5EFD )
+ {
+ if ( iCurrentSector > m_geometry.iSectors )
+ break;
+
+ ReadSector( m_abtCurrBuff, iCurrentSector );
+
+ BOOL bSkip = IsBlockEmpty( m_abtCurrBuff, m_iSectorSize );
+
+ //first non empty sector is marked as first, what a surprise! :)
+ if ( !bSkip && !iFirstSector )
+ {
+ iFirstSector = iCurrentSector;
+ iPrevSector = iCurrentSector;
+ }
+
+ //if just skipped, increment sector
+ if ( bSkip )
+ {
+ iCurrentSector++;
+ }
+ else
+ {
+ //if there is a gap, write sector number
+ if ( ( iCurrentSector - iPrevSector ) > 1 )
+ {
+ *( m_pbtCurr++ ) = iCurrentSector;
+ *( m_pbtCurr++ ) = iCurrentSector >> 8;
+ }
+ else
+ {
+ //else mark previous record
+ *m_pbtLastRec |= 0x80;
+ }
+
+ //first sector could be encoded with only some data
+ if ( iCurrentSector == iFirstSector )
+ EncodeRec( TRUE );
+ else
+ {
+ //if are same, encode as record 46
+ if ( !memcmp( m_abtPrevBuff, m_abtCurrBuff, m_iSectorSize ) )
+ EncodeRec46();
+ else
+ EncodeRec( FALSE );
+ }
+
+ //store this sector as previous
+ memcpy( m_abtPrevBuff, m_abtCurrBuff, m_iSectorSize );
+
+ //and move pointers
+ iPrevSector = iCurrentSector;
+ iCurrentSector++;
+ }
+
+ }
+
+ //mark previous sector
+ *m_pbtLastRec |= 0x80;
+
+ //encode end
+ EncodeRec45();
+
+ BYTE* pEnd = m_pbtCurr;
+
+ //change beginning block
+ if ( iCurrentSector > m_geometry.iSectors )
+ EncodeRecFA( TRUE, iPass, iDensity, iFirstSector );
+ else
+ EncodeRecFA( FALSE, iPass, iDensity, iFirstSector );
+
+ //and write whole pass
+
+ if ( ( pEnd - m_pbtPass ) > 0x6000 )
+ {
+ sprintf( m_szLastError, "DCM: Internal error! Pass too long!" );
+ delete [] m_pbtPass;
+ cf.Close();
+ unlink( szOutFile );
+ return FALSE;
+ }
+
+ if ( !cf.Write( m_pbtPass, pEnd - m_pbtPass ) )
+ {
+ sprintf( m_szLastError, "DCM: Can't write!" );
+ delete [] m_pbtPass;
+ cf.Close();
+ unlink( szOutFile );
+ return FALSE;
+ }
+
+ iPass++;
+ }
+
+ cf.Close();
+
+ delete [] m_pbtPass;
+
+ return TRUE;
+}
+
+void CDcm::EncodeRecFA( BOOL bLast, int iPass, int iDensity, int iFirstSec )
+{
+ m_pbtCurr = m_pbtPass;
+
+ #ifdef _DCM_DUMP_
+ printf( "ERFA: %08lX\n", m_pbtCurr - m_pbtPass );
+ #endif
+
+ m_pbtLastRec = m_pbtCurr;
+
+ BYTE btType = bLast ? 0x80 : 0;
+
+ btType |= ( iDensity & 3 ) << 5;
+
+ btType |= ( iPass & 0x1F );
+
+ *( m_pbtCurr++ ) = DCM_HEADER_SINGLE;
+ *( m_pbtCurr++ ) = btType;
+ *( m_pbtCurr++ ) = iFirstSec;
+ *( m_pbtCurr++ ) = iFirstSec >> 8;
+
+}
+
+void CDcm::EncodeRec45()
+{
+ #ifdef _DCM_DUMP_
+ printf( "ER45: %08lX\n", m_pbtCurr - m_pbtPass );
+ #endif
+
+ m_pbtLastRec = m_pbtCurr;
+ *( m_pbtCurr++ ) = DCM_PASS_END;
+}
+
+void CDcm::EncodeRec46()
+{
+ #ifdef _DCM_DUMP_
+ printf( "ER46: %08lX\n", m_pbtCurr - m_pbtPass );
+ #endif
+
+ m_pbtLastRec = m_pbtCurr;
+ *( m_pbtCurr++ ) = DCM_SAME_AS_BEFORE;
+}
+
+void CDcm::EncodeRec( BOOL bIsFirstSector )
+{
+ #ifdef _DCM_DUMP_
+ printf( "ER: %08lX\n", m_pbtCurr - m_pbtPass );
+ #endif
+
+ m_pbtLastRec = m_pbtCurr;
+
+ BYTE abtBuff41[ 0x300 ];
+ BYTE abtBuff43[ 0x300 ];
+ BYTE abtBuff44[ 0x300 ];
+ BYTE* abtBuff47 = m_abtCurrBuff;
+
+ int iEnd41 = 0x300;
+ int iEnd43 = 0x300;
+ int iEnd44 = 0x300;
+
+ int iBestMethod = DCM_UNCOMPRESSED;
+ int iBestEnd = m_iSectorSize;
+ BYTE* pbtBest = abtBuff47;
+
+ EncodeRec43( abtBuff43, &iEnd43, m_abtCurrBuff, m_iSectorSize );
+
+ if ( !bIsFirstSector )
+ {
+ EncodeRec41( abtBuff41, &iEnd41, m_abtCurrBuff, m_abtPrevBuff, m_iSectorSize );
+ EncodeRec44( abtBuff44, &iEnd44, m_abtCurrBuff, m_abtPrevBuff, m_iSectorSize );
+ }
+
+ if ( iEnd41 < iBestEnd )
+ {
+ iBestMethod = DCM_CHANGE_BEGIN;
+ iBestEnd = iEnd41;
+ pbtBest = abtBuff41;
+ }
+
+ if ( iEnd43 < iBestEnd )
+ {
+ iBestMethod = DCM_COMPRESSED;
+ iBestEnd = iEnd43;
+ pbtBest = abtBuff43;
+ }
+
+ if ( iEnd44 < iBestEnd )
+ {
+ iBestMethod = DCM_CHANGE_END;
+ iBestEnd = iEnd44;
+ pbtBest = abtBuff44;
+ }
+
+ *( m_pbtCurr++ ) = iBestMethod;
+ memcpy( m_pbtCurr, pbtBest, iBestEnd );
+ m_pbtCurr += iBestEnd;
+}
+
+void CDcm::EncodeRec41( BYTE* pbtDest, int* piDestLen, BYTE* pbtSrc, BYTE* pbtSrcOld, int iSrcLen )
+{
+ BYTE* pbtS = pbtSrc + iSrcLen - 1;
+ pbtSrcOld += iSrcLen - 1;
+
+ BYTE* pbtD = pbtDest;
+
+ for( int i = 0; i < iSrcLen; i++ )
+ {
+ if ( *( pbtS-- ) != * ( pbtSrcOld-- ) )
+ break;
+ }
+
+ pbtS++;
+
+ *( pbtD++ ) = pbtS - pbtSrc;
+
+ int iBytes = pbtS - pbtSrc + 1;
+
+ while( iBytes-- )
+ {
+ *( pbtD++ ) = *( pbtS-- );
+ }
+
+ *piDestLen = pbtD - pbtDest;
+}
+
+void CDcm::EncodeRec43( BYTE* pbtDest, int* piDestLen, BYTE* pbtSrc, int iSrcLen )
+{
+ BYTE* pbtEnd = pbtSrc + iSrcLen;
+ BYTE* pbtCur = pbtSrc;
+
+ BYTE* pbtD = pbtDest;
+
+ while( pbtCur < pbtEnd )
+ {
+ BOOL bFound = FALSE;
+
+ for( BYTE* pbtNow = pbtCur; pbtNow < ( pbtEnd - 2 ); pbtNow++ )
+ {
+
+ if ( ( *pbtNow == *(pbtNow+1) ) && ( *pbtNow == *(pbtNow+2) ) )
+ {
+ int iUnc = pbtNow - pbtCur;
+
+ *( pbtD ++ ) = pbtNow - pbtSrc;
+ if ( iUnc )
+ {
+ memcpy( pbtD, pbtCur, iUnc );
+ pbtD += iUnc;
+ }
+
+ BYTE bt = *pbtNow;
+ BYTE*p;
+ for( p = pbtNow + 1; p < pbtEnd; p++ )
+ {
+ if ( *p != bt )
+ break;
+ }
+
+ if ( p > pbtEnd )
+ p = pbtEnd;
+
+ *( pbtD++ ) = p - pbtSrc;
+ *( pbtD++ ) = bt;
+
+ pbtCur = p;
+ bFound = TRUE;
+ break;
+ }
+ }
+
+ if ( ( pbtCur >= pbtEnd - 2 ) || !bFound )
+ {
+ if ( pbtCur < pbtEnd )
+ {
+ *( pbtD++ ) = iSrcLen;
+ memcpy( pbtD, pbtCur, pbtEnd - pbtCur );
+ pbtD += pbtEnd - pbtCur;
+ }
+
+ break;
+ }
+
+ }
+
+ *piDestLen = pbtD - pbtDest;
+}
+
+void CDcm::EncodeRec44( BYTE* pbtDest, int* piDestLen, BYTE* pbtSrc, BYTE* pbtSrcOld, int iSrcLen )
+{
+ BYTE* pbtS = pbtSrc;
+ BYTE* pbtEnd = pbtSrc + iSrcLen;
+
+ BYTE* pbtD = pbtDest;
+
+ for( int i = 0; i < iSrcLen; i++ )
+ {
+ if ( *( pbtS++ ) != * ( pbtSrcOld++ ) )
+ break;
+ }
+
+ pbtS--;
+
+ *( pbtD++ ) = pbtS - pbtSrc;
+ memcpy( pbtD, pbtS, pbtEnd - pbtS );
+ pbtD += pbtEnd - pbtS;
+
+ *piDestLen = pbtD - pbtDest;
+}
+
+#endif // __CDISK_WRITE__
+