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

#define ROOT_DIR 361

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

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

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

	m_pDisk = pDisk;
	m_pRoot = NULL;

	return ReadDir( ROOT_DIR, (CDosMDirEntry**)&m_pRoot );
}

BOOL CDosM::ReadDir( int iSector, CDosMDirEntry** ppRoot )
{
	DOSM_DIRENT dire;

	WORD wEntry = 0;

	CDosMDirEntry* pPrev = NULL;

	do
	{
		BYTE abtSec[ 0x100 ];

		//printf( "reading sec %d\n", iSector + ( wEntry / 8 ) );

		if ( !m_pDisk->ReadSector( abtSec, iSector + ( wEntry / 8 ) ) )
		{
			sprintf( m_szLastError, "DOSM: 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( DOSM_DIRENT ), sizeof( DOSM_DIRENT ) );
		BYTE* pTmp = abtSec + ( wEntry % 8) * sizeof( DOSM_DIRENT );
		dire.btFlags = MGET_B( pTmp );
		dire.wSecCount = MGET_LEW( pTmp );
		dire.wSecStart = MGET_LEW( pTmp );
		memcpy( dire.acAtariName, pTmp, 11 );

		//printf( "%p %p %04X %02X\n", pTmp, abtSec, wEntry, dire.btFlags );

		if ( !dire.btFlags )
			break;

		CDosMDirEntry* pE = CreateEntry( &dire );

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

		}

		wEntry++;

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

	return TRUE;
}

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

CDosMDirEntry* CDosM::CreateEntry( DOSM_DIRENT* pDire )
{
	CDosMDirEntry* pE = new CDosMDirEntry();

	if ( !pE )
	{
		sprintf( m_szLastError, "DOSM: 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 );
	//printf( "%02X %04X %04X %s\n", pDire->btFlags, pDire->wSecStart, pDire->wSecCount, pE->m_szFname );

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

	if ( pDire->btFlags == 0x10 )
	{
		pE->m_dwFlags |= DIRE_SUBDIR;

		BOOL bRes = ReadDir( pE->m_wSecStart, (CDosMDirEntry**)&pE->m_pSubdir );

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

	return pE;
}

BOOL CDosM::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, "DOSM: Unable to create file '%s'!", szOutFile );
			return FALSE;
		}
	}

	BYTE abtBuff[ 0x0100 ];

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

	abtBuff[ wSectorSize - 1 ] = 0;

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

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

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

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

		//originally, there was 0x07 as a mask, but megaimages were not working with it
		if ( m_pDisk->GetSectorCount() > 943 )
			wSector = abtBuff[ wSectorSize - 2 ] + ( (WORD)abtBuff[ wSectorSize - 3 ] & 0xff ) * 0x100;
		else
			wSector = abtBuff[ wSectorSize - 2 ] + ( 0x03 & (WORD)abtBuff[ wSectorSize - 3 ] ) * 0x100;

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

		wCount--;
	}

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

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

	return TRUE;
}