/*
    CaPriCe Forever - Amstrad CPC 464/664/6128 emulator
    Copyright (C) 2015-2019 by Frédéric Coste

    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 3 of the License, or
    (at your option) 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, see <http://www.gnu.org/licenses/>.
*/


#include "..\Native_CPC.h"


/*
** Make sure we can call this stuff from C++.
*/
#ifdef __cplusplus
extern "C" {
#endif


//===================
// PATCH begin
#ifdef _PATCH_ENABLE

#endif /* _PATCH_ENABLE */
// PATCH end
//===================


#define FILE_ENTRY_SIZE                32


tULong ROMDOS_GetFirstSectorOfBlock(tDiskOperation* DiskOperation, tULong Block);
tULong ROMDOS_GetNextSector(tDiskOperation* DiskOperation, tULong CHRN);


tULong ROMDOS_DiskGetFirstSector(tDiskOperation* DiskOperation)
/***********************************************************************
 *
 *  ROMDOS_DiskGetFirstSector
 *
 ***********************************************************************/
{
tTrack* actualTrackP;
tSector* sectorP;
tULong indexSector;
tULong loopSector;
tULong loopEntry;
tULong loopChar;
tULong CHRN;

  actualTrackP = &DiskOperation->Drive->track[DiskOperation->CatalogueTrack][0]; // Side = 0

  // Only find file entries into four first sectors.
  indexSector = 1; // Begin to find the first sector
  CHRN = 0L;
  while ( (indexSector <= 8) && (CHRN == 0L) )
  {
    tULong nbofentries;
    tUChar* fileEntryP;

    //
    // Find sector
    //
    sectorP = 0;
    for (loopSector=0; loopSector < actualTrackP->sectors; loopSector++)
    {
      if ( ((actualTrackP->sector[loopSector].CHRN[SECTOR_CHRN_R] & 0x0FU) == indexSector)
           && (actualTrackP->sector[loopSector].CHRN[SECTOR_CHRN_N] == 2) )
      {
        sectorP = &actualTrackP->sector[loopSector];
        break;
      }
    }

  	// Do not take into account empty or missing sector
  	if (!sectorP || (!sectorP->size) )
  	{
  		// Next sector
  		indexSector++;
  	 	continue;
  	}

    // Prepare sector read
    nbofentries = sectorP->declared_size / FILE_ENTRY_SIZE; // 32 bytes per file entry
  	fileEntryP = sectorP->data; // Point at beginning of sector.

    for (loopEntry=0; loopEntry < nbofentries; loopEntry++, fileEntryP += FILE_ENTRY_SIZE)
    {
      tUChar* bytePtr;
      tUChar* fileStoredEntryP;
      
      //
      // Parse File Entry
      //
      // Entry documentation
      // ===================
      // Byte 0x0 = USER; 229(0xe5) for deleted files, 0 by default.
      // Byte 0x1 to 0x8 = filename
      // Byte 0x9 to 0xb = extension. Byte 0x9 bit7 = 1 = Read Only. Byte 0xa bit7 = 1 = Hidden.
      // Byte 0xc = entry index (First entry = 0)
      // Byte 0xd to 0xe = Not used ??
      // Byte 0xf = size in records (1 record=128 bytes). 0x80 for full entry => check another entry
      // Byte 0x10 to 0x1f = list of numbers of 1k blocks (2 sectors length).

      // How to locate a block
      // =====================
      // Cylinder (C) = INT(block * 2 / 9); 9 sectors per track !! For EXTENDED, check number of sectors per Tracks
      // Head     (H) = Always 0
      // Sector   (R) = (block * 2) MODULO 9 + 1 + TYPE (0xC0 for DATA, 0x40 for VENDOR)
      // Size     (N) = Size (*256 bytes)

      // Entry size is non null
      if (!fileEntryP[0xf])
        continue;

      // First entry ?
      if (fileEntryP[0xc])
        continue;

    	// Compare filename and extension
  	  fileStoredEntryP = DiskOperation->CatalogueEntries[DiskOperation->CatalogueEntryIndex];
    	bytePtr = &fileEntryP[0x1]; // Point at the beginning of the filename.
    	for (loopChar=0; loopChar < 11; loopChar++)
    	{
    		if ((*bytePtr++ & 0x7f) != *fileStoredEntryP++)
    		  break;
      }
      // Matching filename ?
      // Matching USER
      if ( (loopChar == 11)
           && (fileEntryP[0x0] == DiskOperation->CatalogueEntryUser[DiskOperation->CatalogueEntryIndex]) )
      {
        CHRN = ROMDOS_GetFirstSectorOfBlock(DiskOperation, (fileEntryP[0x10] + (fileEntryP[0x11] << 8)));
        break;
      }
    }

    indexSector++; // Next sector
  }

  DiskOperation->CatalogueLastCHRN = CHRN;
  
  return CHRN;
}
/*----------------------------------------------------------------------------*/


tULong ROMDOS_GetFirstSectorOfBlock(tDiskOperation* DiskOperation, tULong Block)
/***********************************************************************
 *
 *  ROMDOS_GetFirstSectorOfBlock
 *
 ***********************************************************************/
{
  tULong CHRN;
  tUChar Cylinder;
  tUChar Head;
  tUChar Sector;
  tUChar Size;
  tUChar SectorsPerBlock;
  tUChar SectorsPerTrack;

  Head = 0;
  Size = 2; // 512 bytes
  SectorsPerBlock = 4;

  // ROMDOS D10
  if (DiskOperation->FormatType == DISK_FORMAT_ROMDOS_D10)
  {
    Cylinder = (Block * SectorsPerBlock) / 20; // 10 sectors per side. 2 sides
    Sector = (Block * SectorsPerBlock) - (Cylinder * 20) + 1;
    if (Sector > 10)
    {
      Head++;
      Sector -= 10;
    }
    // Add sector Mask
    Sector |= 0x10;
  }
  // ROMDOS D1
  else if (DiskOperation->FormatType == DISK_FORMAT_ROMDOS_D1)
  {
    Cylinder = (Block * SectorsPerBlock) / 18; // 9 sectors per side. 2 sides
    Sector = (Block * SectorsPerBlock) - (Cylinder * 18) + 1;
    if (Sector > 9)
    {
      Head++;
      Sector -= 9;
    }
    // Add sector Mask
    Sector |= 0x00;
  }

  CHRN  = Cylinder << 24;
  CHRN |= Head << 16;
  CHRN |= Sector << 8;
  CHRN |= Size << 0;

  return CHRN; 
}
/*----------------------------------------------------------------------------*/


tULong ROMDOS_GetNextSector(tDiskOperation* DiskOperation, tULong CHRN)
/***********************************************************************
 *
 *  ROMDOS_GetNextSector
 *
 ***********************************************************************/
{
  tUChar SectorsPerTrack;
  tUChar Cylinder;
  tUChar Head;
  tUChar Sector;
  tUChar Size;

  Cylinder = (CHRN >> 24) & 0xff;
  Head     = (CHRN >> 16) & 0xff;
  Sector   = (CHRN >> 8) & 0xff;
  Size     = (CHRN >> 0) & 0xff;

  // ROMDOS D10
  if (DiskOperation->FormatType == DISK_FORMAT_ROMDOS_D10)
  {
    SectorsPerTrack = 10;
  }
  // ROMDOS D1
  else if (DiskOperation->FormatType == DISK_FORMAT_ROMDOS_D1)
  {
    SectorsPerTrack = 9;
  }

  // Increment Sector
  Sector++;
  if ( (Sector & 0x0f) > SectorsPerTrack)
  {
    // First sector of next head
    Head++;
    Head &= 1;
    // Next cylinder ?
    if (!Head)
    {
      Cylinder++;
    }

    Sector = (Sector & 0xf0) + 1;
  }
  
  CHRN  = Cylinder << 24;
  CHRN |= Head << 16;
  CHRN |= Sector << 8;
  CHRN |= Size << 0;

  return CHRN; 
}
/*----------------------------------------------------------------------------*/


tSector* ROMDOS_FindSector(tDiskOperation* DiskOperation, tULong CHRN)
/***********************************************************************
 *
 *  ROMDOS_FindSector
 *
 ***********************************************************************/
{
  tSector* FoundSector;
  tTrack* TrackP;
  tUChar loopSector;
  tUChar Cylinder;
  tUChar Head;
  tUChar Sector;
  tUChar Size;

  Cylinder = (CHRN >> 24) & 0xff;
  Head     = (CHRN >> 16) & 0xff;
  Sector   = (CHRN >> 8) & 0xff;
  Size     = (CHRN >> 0) & 0xff;

  // Find sector
  FoundSector = NULL;
  TrackP = &DiskOperation->Drive->track[Cylinder][Head];
  for (loopSector=0; loopSector < (tUChar)TrackP->sectors; loopSector++)
  {
    tSector* SectorP = &TrackP->sector[loopSector];
    if ( (SectorP->CHRN[SECTOR_CHRN_R] == Sector)
         && (SectorP->CHRN[SECTOR_CHRN_N] == Size) )
    {
      FoundSector = SectorP;
      break;
    }
  }

  return FoundSector;
}
/*----------------------------------------------------------------------------*/


#ifdef __cplusplus
}  /* End of the 'extern "C"' block */
#endif
