//    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 "cfs_dos2.h"
#include "autil.h"

#define ROOT_DIR 361

CDos2::CDos2() : CFs()
{
	#ifdef _MEMORY_DUMP_
		printf( "CDos2 constructed: %08X\n", this );
	#endif
}

CDos2::~CDos2()
{
	Dismount();
	#ifdef _MEMORY_DUMP_
		printf( "CDos2 destructed: %08X\n", this );
	#endif
}

BOOL CDos2::Mount( ADisk* pDisk )
{
	m_iFilesValid = 0;
	m_iFilesInvalid = 0;

	m_pDisk = pDisk;
	m_pRoot = NULL;

	DOS2_DIRENT dire;

	WORD wEntry = 0;

	CDos2DirEntry* pPrev = NULL;

	do
	{
		BYTE abtSec[ 0x100 ];

		if ( !m_pDisk->ReadSector( abtSec, ROOT_DIR + ( wEntry / 8 ) ) )
		{
			sprintf( m_szLastError, "DOS2: Can't read directory entry %04X because\n%s", wEntry, m_pDisk->GetLastError() );
			return FALSE;
		}

		//this is not endian-safe
		//memcpy( &dire, abtSec + ( wEntry % 8) * sizeof( DOS2_DIRENT ), sizeof( DOS2_DIRENT ) );
		BYTE* pTmp = abtSec + ( wEntry % 8) * sizeof( DOS2_DIRENT );
		dire.btFlags = MGET_B( pTmp );
		dire.wSecCount = MGET_LEW( pTmp );
		dire.wSecStart = MGET_LEW( pTmp );
		memcpy( dire.acAtariName, pTmp, 11 );

		if ( !dire.btFlags )
			break;

		CDos2DirEntry* pE = CreateEntry( &dire, wEntry );

		if ( pE && ! ( pE->m_dwFlags & DIRE_DELETED ) )
		{
			if ( m_pRoot )
			{
				pPrev->m_pNext = pE;
				pE->m_pPrev = pPrev;
				pPrev = pE;
			}
			else
			{
				m_pRoot = pE;
				pPrev = pE;
			}

		}

		wEntry++;

	} while( dire.btFlags && ( wEntry < 64 ) );

	return TRUE;
}

void CDos2::Dismount()
{
	DeleteList( m_pRoot );
}

CDos2DirEntry* CDos2::CreateEntry( DOS2_DIRENT* pDire, WORD wEntry )
{
	CDos2DirEntry* pE = new CDos2DirEntry();

	if ( !pE )
	{
		sprintf( m_szLastError, "DOS2: Can't allocate memory for directory!" );
		return NULL;
	}

	if ( pDire->btFlags == 0x80 )
	{
		pE->m_dwFlags |= DIRE_DELETED;
	}

	ADos2MsDos( pE->m_szFname, pDire->acAtariName );

	sprintf( pE->m_szAscData, "%02X %04X %04X", pDire->btFlags, pDire->wSecStart, pDire->wSecCount );

	pE->m_wFileNumber = wEntry;
	pE->m_btFlags = pDire->btFlags;
	pE->m_wSecStart = pDire->wSecStart;
	pE->m_wSecCount = pDire->wSecCount;

	if ( pE->m_dwFlags & DIRE_DELETED )
	{
	}
	else if ( ExportFile( NULL, pE ) )
		m_iFilesValid++;
	else
		m_iFilesInvalid++;

	return pE;
}

BOOL CDos2::ExportFile( char* szOutFile, CDirEntry* pDirE )
{
	int hOutfile = -1;

	if ( szOutFile )
	{
		hOutfile = open( szOutFile, O_BINARY | O_CREAT | O_TRUNC | O_WRONLY, 0666 );

		if ( -1 == hOutfile )
		{
			sprintf( m_szLastError, "DOS2: Unable to create file '%s'!", szOutFile );
			return FALSE;
		}
	}

	BYTE abtBuff[ 0x0100 ];

	WORD wSector = ((CDos2DirEntry*)pDirE ) -> m_wSecStart;
	WORD wCount = ((CDos2DirEntry*)pDirE ) -> m_wSecCount;
	WORD wSectorSize = m_pDisk->GetSectorSize();
	WORD wFileNumber = ((CDos2DirEntry*)pDirE ) -> m_wFileNumber;

	abtBuff[ wSectorSize - 1 ] = 0;

	while( wCount )
	{
		if ( wSector < 1 )
		{
			sprintf( m_szLastError, "DOS2: Corrupted file '%s' (invalid sector %04X)", szOutFile, wSector );
			return FALSE;
		}

		if ( ( abtBuff[ wSectorSize - 1 ] & 0x80 ) && ( wSectorSize == 0x80 ) )
		{
			sprintf( m_szLastError, "DOS2: Corrupted file '%s' (unexpected EOF)", szOutFile );
			return FALSE;
		}

		if ( !m_pDisk->ReadSector( abtBuff, wSector ) )
		{
			sprintf( m_szLastError, "DOS2: Corrupted file '%s'\n%s\n", szOutFile, m_pDisk->GetLastError() );
			return FALSE;
		}

		/*
		if ( -1 != hOutfile )
		{
			printf( "%04X/%04X %02X | %02X %02X %02X\n", 
				wSector,
				wCount,
				wFileNumber,
				abtBuff[ wSectorSize - 3 ], 
				abtBuff[ wSectorSize - 2 ],
				abtBuff[ wSectorSize - 1 ] );
		}
		*/

		wSector = abtBuff[ wSectorSize - 2 ] + ( 0x03 & abtBuff[ wSectorSize - 3 ] ) * 0x100;

		if ( ( abtBuff[ wSectorSize-3 ] >> 2 ) != wFileNumber )
		{
			WORD wFN = abtBuff[ wSectorSize - 3 ] >> 2;

			sprintf( m_szLastError, "DOS2: Corrupted file '%s' (167: file number mismatch [%04X != %04X])", szOutFile, wFileNumber, wFN );
			return FALSE;
		}


		if ( -1 != hOutfile )
			write( hOutfile, abtBuff, abtBuff[ wSectorSize - 1 ] );

		wCount--;
	}

	if ( ((CDos2DirEntry*)pDirE ) -> m_wSecCount )
	{
		if ( ! ( abtBuff[ wSectorSize - 1 ] & 128 ) && ( wSectorSize == 128 ) && wSector)
		{
			sprintf( m_szLastError, "DOS2: Corrupted file '%s' (expected EOF, code %02X, next sector %04X)", szOutFile, abtBuff[ wSectorSize - 1 ], wSector );
			return FALSE;
		}
	}

	if ( -1 != hOutfile )
		close( hOutfile );

	return TRUE;
}