/*
    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

#undef PATCH_1
#undef PATCH_2
#define PATCH_1
#define PATCH_2

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


#define FILE_ENTRY_SIZE                32




tVoid ROMDOS_DiskReadCatalogue(tDiskOperation* DiskOperation)
/***********************************************************************
 *
 *  ROMDOS_DiskReadCatalogue
 *
 ***********************************************************************/
{
tDrive* driveP;
tTrack* TrackP;
tSector* sectorP;
tULong indexSector;
tULong loopSector;
tULong loopEntry;
tULong loopCatalogEntry;
tULong loopChar;

  driveP = DiskOperation->Drive;


  //
  // Parse catalog from Track 0 to Track 2 until valid entries detected
  //
  DiskOperation->NbCatalogueEntries = 0;
  TrackP = &driveP->track[0][0]; // Track 0, Side = 0

  do
  {
    // Do not take into account empty or missing tracks
    if (!TrackP->size)
      continue;

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

      //
      // Find sector
      //
      sectorP = 0;
      for (loopSector=0; loopSector < TrackP->sectors; loopSector++)
      {
    	  if ( ((TrackP->sector[loopSector].CHRN[SECTOR_CHRN_R] & 0x0FU) == indexSector)
             && (TrackP->sector[loopSector].CHRN[SECTOR_CHRN_N] == 2) )
    	  {
          sectorP = &TrackP->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;
        tBool FilenameFound;

        //
        // PARADOS File Entry ?????
        //
        // Entry documentation
        // ===================
        // Byte 0x0 = USER; 0-15, 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 = 0 (= 1 for DAMS.BAS)
				// Byte 0xe = 0 (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 2k blocks (4 sectors length). 2 bytes per block (LSB MSB)

        // 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 + MASK
				// Size     (N) = Size (*256 bytes)

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

        // Check USER
        if ( (fileEntryP[0x0] > 15) && (fileEntryP[0x0] != 0xe5) )
          continue;

				// Check ZERO bytes
#ifndef PATCH_2
				if ( (fileEntryP[0xd] != 0) || (fileEntryP[0xe] != 0) )
					continue;
#else /* PATCH_2 */
				if (fileEntryP[0xe] != 0)
					continue;
#endif /* PATCH_2 */

        // Check filename
				bytePtr = &fileEntryP[0x1]; // Point at the beginning of the filename.
#ifndef PATCH_1
				while (    (*bytePtr >= 0x20 /*SPACE*/)
								&& (*bytePtr <= 0x5a /*Z*/)
								&& (bytePtr < (fileEntryP+0x9)) )
#else /* PATCH_1 */
				while (    ((*bytePtr & 0x7f) >= 0x20 /*SPACE*/)
								&& ((*bytePtr & 0x7f) <= 0x5a /*Z*/)
								&& (bytePtr < (fileEntryP+0x9)) )
#endif /* PATCH_1 */
				{
					bytePtr++; // Loop until non printable character
        }
        if (bytePtr < (fileEntryP+0x9))
          continue;

        // Check extension
        bytePtr = &fileEntryP[0x9]; // Point at the beginning of the filename.
        while (    ((*bytePtr & 0x7f) >= 0x20 /*SPACE*/)
                && ((*bytePtr & 0x7f) <= 0x5a /*Z*/)
                && (bytePtr < (fileEntryP+0xc)) )
        {
          bytePtr++; // Loop until non printable character
        }
        if (bytePtr < (fileEntryP+0xc))
          continue;

        // Check filename with valid letters/figures
        bytePtr = &fileEntryP[0x1]; // Point at the beginning of the filename.
        while (bytePtr <= (fileEntryP+0xc))
        {
          // Figure ?
          if ( ((*bytePtr & 0x7f) >= 0x30 /*0*/) && ((*bytePtr & 0x7f) <= 0x39 /*9*/) ) break;
          // Letter ?
          if ( ((*bytePtr & 0x7f) >= 0x41 /*A*/) && ((*bytePtr & 0x7f) <= 0x5a /*Z*/) ) break;
          bytePtr++;
        }
        // No valid characters ?
        if (bytePtr > (fileEntryP + 0xc))
          continue;

        // Keep Catalog track
        DiskOperation->CatalogueTrack = 0;

        //
        // Entry is valid
        //
        FilenameFound = cFalse;
        // Search already stored filename
        for (loopCatalogEntry=0;
             loopCatalogEntry < DiskOperation->NbCatalogueEntries;
             loopCatalogEntry++)
        {
    		  // Compare filename and extension
  	      tUChar* fileStoredEntryP = DiskOperation->CatalogueEntries[loopCatalogEntry];
    	    bytePtr = &fileEntryP[0x1]; // Point at the beginning of the filename.
    		  for (loopChar=0; loopChar < 11; loopChar++)
    		  {
    		    if ((*bytePtr++ & 0x7f) != *fileStoredEntryP++)
    		      break;
    		  }
          // Matching filename ?
          // AND Matching USER
          if ( (loopChar == 11)
               && (fileEntryP[0x0] == DiskOperation->CatalogueEntryUser[loopCatalogEntry]) )
          {
            tUChar loopBlocks;
            tUChar numberBlocks = 0;

            // Get number of blocks
            for (loopBlocks=0; loopBlocks < 16; loopBlocks+=2)
            {
              tULong BlockNum = (fileEntryP[0x11+loopBlocks] << 8) + fileEntryP[0x10+loopBlocks];
              if (BlockNum)
              {
                numberBlocks++;
              }
            }

            FilenameFound = cTrue;

            // Update file size
            DiskOperation->CatalogueEntrySize[loopCatalogEntry] += numberBlocks * 2024;
            break;
          }
        }

        // Add new entry
        if (FilenameFound == cFalse)
        {
          tUChar* fileStoredEntryP = DiskOperation->CatalogueEntries[DiskOperation->NbCatalogueEntries];
          tUChar loopBlocks;
          tUChar numberBlocks = 0;

          // Add filename + extension
    	    bytePtr = &fileEntryP[0x1]; // Point at the beginning of the filename.
          for (loopChar=0; loopChar < 11; loopChar++)
          {
            *fileStoredEntryP++ = *bytePtr++ & 0x7f;  // Do not copy Read-only and Hidden flags
          }

          // Get number of blocks
          for (loopBlocks=0; loopBlocks < 16; loopBlocks+=2)
          {
            tULong BlockNum = (fileEntryP[0x11+loopBlocks] << 8) + fileEntryP[0x10+loopBlocks];
            if (BlockNum)
            {
              numberBlocks++;
            }
          }

          // Add file info
          DiskOperation->CatalogueEntrySize[DiskOperation->NbCatalogueEntries] = numberBlocks * 2024;
          DiskOperation->CatalogueEntryUser[DiskOperation->NbCatalogueEntries] = fileEntryP[0x0];

          // Update number of entries
          DiskOperation->NbCatalogueEntries++;
        }
      }

      indexSector++; // Next sector
    }
  }
  while (0);
}
/*----------------------------------------------------------------------------*/

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