@@ -1385,22 +1385,24 @@ std::vector<directoryEntry> d64::directory()
13851385// / <returns>true if valid</returns>
13861386bool d64::validateD64 ()
13871387{
1388- // Check file size
13891388 auto sz = disktype == diskType::thirty_five_track ? D64_DISK35_SZ : D64_DISK40_SZ ;
13901389 if (data.size () != sz) {
1391- throw std::runtime_error (" Error: Invalid .d64 size (" + std::to_string (data.size ()) + " bytes)" );
1390+ std::cerr << " Error: Invalid .d64 size (" << data.size () << " bytes), expected " << sz << " bytes\n " ;
1391+ return false ;
13921392 }
13931393
1394- // Check BAM structure
13951394 if (diskBamPtr->dirStart .track != DIRECTORY_TRACK || diskBamPtr->dirStart .sector != DIRECTORY_SECTOR ) {
1396- throw std::runtime_error (" Error: BAM structure is invalid (Incorrect directory track/sector)" );
1395+ std::cerr << " Warning: BAM structure has non-standard directory track/sector ("
1396+ << static_cast <int >(diskBamPtr->dirStart .track ) << " /"
1397+ << static_cast <int >(diskBamPtr->dirStart .sector ) << " ). "
1398+ << " This may be intentional for copy protection or custom formats.\n " ;
13971399 }
13981400
1399- // Check sector data integrity (optional deeper check)
14001401 auto dir = getTrackSectorPtr (DIRECTORY_TRACK , DIRECTORY_SECTOR );
1401- auto valid = dir->track == DIRECTORY_TRACK || ( dir->track == 0 && dir-> sector == 0xFF );
1402+ auto valid = ( dir->track == DIRECTORY_TRACK || dir->track == 0 );
14021403 if (!valid) {
1403- throw std::runtime_error (" Error: Directory sector does not match expected values" );
1404+ std::cerr << " Warning: Directory sector pointer has non-standard next track ("
1405+ << static_cast <int >(dir->track ) << " ).\n " ;
14041406 }
14051407
14061408 return true ;
@@ -1414,25 +1416,21 @@ bool d64::validateD64()
14141416// / <returns>true if successful</returns>
14151417std::vector<trackSector> d64::parseSideSectors (int sideTrack, int sideSector)
14161418{
1417- std::vector<trackSector> recordMap; // Store TrackSector
1419+ std::vector<trackSector> recordMap;
14181420
1419- // sideTrack 0 signifies end
14201421 while (sideTrack != 0 ) {
14211422 auto sideSectorPtr = getSideSectorPtr (sideTrack, sideSector);
14221423
1423- // Get Next side-sector location
14241424 uint8_t nextTrack = sideSectorPtr->next .track ;
14251425 uint8_t nextSector = sideSectorPtr->next .sector ;
14261426
1427- // Read record-to-sector mappings
14281427 for (auto i = 0 ; i < SIDE_SECTOR_CHAIN_SZ ; ++i) {
14291428 if (sideSectorPtr->chain [i].track == 0 )
1430- break ; // End of records
1429+ break ;
14311430
14321431 recordMap.emplace_back (sideSectorPtr->chain [i]);
14331432 }
14341433
1435- // Move to next side sector
14361434 sideTrack = nextTrack;
14371435 sideSector = nextSector;
14381436 }
@@ -1457,3 +1455,286 @@ bool d64::writeData(int track, int sector, std::vector<uint8_t> bytes, int byteo
14571455 }
14581456 return false ;
14591457}
1458+
1459+ int d64::getRecordCount (std::string_view filename) {
1460+ auto fileEntry = findFile (filename);
1461+ if (!fileEntry.has_value () || fileEntry.value ()->file_type .type != d64FileTypes::REL ) return 0 ;
1462+
1463+ int recordLength = fileEntry.value ()->recordLength ;
1464+ if (recordLength == 0 ) return 0 ;
1465+
1466+ int totalPayloadBytes = 0 ;
1467+ trackSector sidePosition = fileEntry.value ()->side ;
1468+
1469+ while (sidePosition.track != 0 ) {
1470+ auto side = getSideSectorPtr (sidePosition.track , sidePosition.sector );
1471+ for (int i = 0 ; i < SIDE_SECTOR_CHAIN_SZ ; ++i) {
1472+ if (side->chain [i].track == 0 ) break ;
1473+ auto dataSectorPos = side->chain [i];
1474+ auto dataSector = getSectorPtr (dataSectorPos.track , dataSectorPos.sector );
1475+ if (dataSector->next .track != 0 ) {
1476+ totalPayloadBytes += 254 ;
1477+ } else {
1478+ totalPayloadBytes += dataSector->next .sector - 1 ;
1479+ break ;
1480+ }
1481+ }
1482+ sidePosition = side->next ;
1483+ }
1484+
1485+ return totalPayloadBytes / recordLength;
1486+ }
1487+
1488+ int d64::getRecordSize (std::string_view filename) {
1489+ auto fileEntry = findFile (filename);
1490+ if (!fileEntry.has_value () || fileEntry.value ()->file_type .type != d64FileTypes::REL ) return 0 ;
1491+ return fileEntry.value ()->recordLength ;
1492+ }
1493+
1494+ std::optional<std::vector<uint8_t >> d64::readRecord (std::string_view filename, int recordNumber) {
1495+ auto fileEntry = findFile (filename);
1496+ if (!fileEntry.has_value () || fileEntry.value ()->file_type .type != d64FileTypes::REL ) return std::nullopt ;
1497+
1498+ int recordLength = fileEntry.value ()->recordLength ;
1499+ if (recordLength == 0 ) return std::nullopt ;
1500+
1501+ int recordCount = getRecordCount (filename);
1502+ if (recordNumber < 1 || recordNumber > recordCount) return std::nullopt ;
1503+
1504+ int byteOffset = (recordNumber - 1 ) * recordLength;
1505+ int sectorIndex = byteOffset / 254 ;
1506+ int byteOffsetInSector = (byteOffset % 254 ) + 2 ;
1507+
1508+ int sideSectorIndex = sectorIndex / 120 ;
1509+ int pointerIndex = sectorIndex % 120 ;
1510+
1511+ trackSector sidePosition = fileEntry.value ()->side ;
1512+ sideSectorPtr side = nullptr ;
1513+
1514+ for (int i = 0 ; i <= sideSectorIndex; ++i) {
1515+ if (sidePosition.track == 0 ) return std::nullopt ;
1516+ side = getSideSectorPtr (sidePosition.track , sidePosition.sector );
1517+ sidePosition = side->next ;
1518+ }
1519+
1520+ if (!side) return std::nullopt ;
1521+ trackSector dataSectorPos = side->chain [pointerIndex];
1522+ if (dataSectorPos.track == 0 ) return std::nullopt ;
1523+
1524+ auto dataSector = getSectorPtr (dataSectorPos.track , dataSectorPos.sector );
1525+ std::vector<uint8_t > recordData (recordLength);
1526+
1527+ int bytesToRead = recordLength;
1528+ int currentOffset = byteOffsetInSector;
1529+ int recordOffset = 0 ;
1530+
1531+ while (bytesToRead > 0 ) {
1532+ int bytesAvailableInSector = 256 - currentOffset;
1533+
1534+ int bytesToCopy = std::min (bytesToRead, bytesAvailableInSector);
1535+
1536+ std::copy_n (dataSector->data .begin () + currentOffset - 2 , bytesToCopy, recordData.begin () + recordOffset);
1537+
1538+ bytesToRead -= bytesToCopy;
1539+ recordOffset += bytesToCopy;
1540+
1541+ if (bytesToRead > 0 ) {
1542+ dataSectorPos = dataSector->next ;
1543+ if (dataSectorPos.track == 0 ) return std::nullopt ;
1544+ dataSector = getSectorPtr (dataSectorPos.track , dataSectorPos.sector );
1545+ currentOffset = 2 ;
1546+ }
1547+ }
1548+
1549+ return recordData;
1550+ }
1551+
1552+ bool d64::expandRelFile (std::string_view filename, int requiredBytes) {
1553+ auto fileEntry = findFile (filename);
1554+ if (!fileEntry.has_value () || fileEntry.value ()->file_type .type != d64FileTypes::REL ) return false ;
1555+
1556+ int totalPayloadBytes = 0 ;
1557+ trackSector sidePosition = fileEntry.value ()->side ;
1558+ sideSectorPtr side = nullptr ;
1559+ trackSector lastSidePosition = {0 , 0 };
1560+ trackSector lastDataSectorPos = {0 , 0 };
1561+ int lastChainIndex = -1 ;
1562+
1563+ while (sidePosition.track != 0 ) {
1564+ lastSidePosition = sidePosition;
1565+ side = getSideSectorPtr (sidePosition.track , sidePosition.sector );
1566+ for (int i = 0 ; i < SIDE_SECTOR_CHAIN_SZ ; ++i) {
1567+ if (side->chain [i].track == 0 ) break ;
1568+ lastDataSectorPos = side->chain [i];
1569+ lastChainIndex = i;
1570+ auto dataSector = getSectorPtr (lastDataSectorPos.track , lastDataSectorPos.sector );
1571+ if (dataSector->next .track != 0 ) {
1572+ totalPayloadBytes += 254 ;
1573+ } else {
1574+ totalPayloadBytes += dataSector->next .sector - 1 ;
1575+ break ;
1576+ }
1577+ }
1578+ sidePosition = side->next ;
1579+ }
1580+
1581+ if (totalPayloadBytes >= requiredBytes) return true ;
1582+
1583+ int bytesToAdd = requiredBytes - totalPayloadBytes;
1584+
1585+ if (lastDataSectorPos.track != 0 ) {
1586+ auto lastDataSector = getSectorPtr (lastDataSectorPos.track , lastDataSectorPos.sector );
1587+ int currentSectorSize = lastDataSector->next .sector - 1 ;
1588+ if (currentSectorSize < 254 ) {
1589+ int toAdd = std::min (bytesToAdd, 254 - currentSectorSize);
1590+ std::fill_n (lastDataSector->data .begin () + currentSectorSize, toAdd, 0 );
1591+
1592+ bytesToAdd -= toAdd;
1593+ currentSectorSize += toAdd;
1594+
1595+ if (bytesToAdd == 0 ) {
1596+ lastDataSector->next .sector = currentSectorSize + 1 ;
1597+ return true ;
1598+ } else {
1599+ lastDataSector->next .sector = 255 ;
1600+ }
1601+ }
1602+ } else {
1603+ return false ;
1604+ }
1605+
1606+ while (bytesToAdd > 0 ) {
1607+ int nextTrack = 0 , nextSector = 0 ;
1608+ if (!findAndAllocateFreeSector (nextTrack, nextSector)) {
1609+ return false ;
1610+ }
1611+
1612+ auto lastDataSector = getSectorPtr (lastDataSectorPos.track , lastDataSectorPos.sector );
1613+ lastDataSector->next .track = nextTrack;
1614+ lastDataSector->next .sector = nextSector;
1615+
1616+ lastDataSectorPos = {static_cast <uint8_t >(nextTrack), static_cast <uint8_t >(nextSector)};
1617+ auto newDataSector = getSectorPtr (lastDataSectorPos.track , lastDataSectorPos.sector );
1618+ std::fill (newDataSector->data .begin (), newDataSector->data .end (), 0 ); // Fill with zeros
1619+
1620+ int toAdd = std::min (bytesToAdd, 254 );
1621+ bytesToAdd -= toAdd;
1622+
1623+ newDataSector->next .track = 0 ;
1624+ newDataSector->next .sector = toAdd + 1 ;
1625+
1626+ lastChainIndex++;
1627+ if (lastChainIndex >= SIDE_SECTOR_CHAIN_SZ ) {
1628+ int newSideTrack = 0 , newSideSector = 0 ;
1629+ if (!findAndAllocateFreeSector (newSideTrack, newSideSector)) return false ;
1630+
1631+ side->next .track = newSideTrack;
1632+ side->next .sector = newSideSector;
1633+
1634+ auto newSide = getSideSectorPtr (newSideTrack, newSideSector);
1635+ std::fill (reinterpret_cast <uint8_t *>(newSide), reinterpret_cast <uint8_t *>(newSide) + SECTOR_SIZE , 0 );
1636+ newSide->recordsize = fileEntry.value ()->recordLength ;
1637+ newSide->block = side->block + 1 ;
1638+
1639+ int sideSectorsCount = newSide->block + 1 ;
1640+ if (sideSectorsCount > SIDE_SECTOR_ENTRY_SIZE ) return false ;
1641+
1642+ trackSector iterSidePos = fileEntry.value ()->side ;
1643+ while (iterSidePos.track != 0 ) {
1644+ auto iterSide = getSideSectorPtr (iterSidePos.track , iterSidePos.sector );
1645+ iterSide->sideSectors [newSide->block ] = {static_cast <uint8_t >(newSideTrack), static_cast <uint8_t >(newSideSector)};
1646+ iterSidePos = iterSide->next ;
1647+ }
1648+
1649+ side = newSide;
1650+ lastChainIndex = 0 ;
1651+
1652+ uint16_t currentSize = fileEntry.value ()->fileSize [0 ] | (fileEntry.value ()->fileSize [1 ] << 8 );
1653+ currentSize++;
1654+ fileEntry.value ()->fileSize [0 ] = currentSize & 0xFF ;
1655+ fileEntry.value ()->fileSize [1 ] = currentSize >> 8 ;
1656+ }
1657+
1658+ side->chain [lastChainIndex] = lastDataSectorPos;
1659+
1660+ uint16_t currentSize = fileEntry.value ()->fileSize [0 ] | (fileEntry.value ()->fileSize [1 ] << 8 );
1661+ currentSize++;
1662+ fileEntry.value ()->fileSize [0 ] = currentSize & 0xFF ;
1663+ fileEntry.value ()->fileSize [1 ] = currentSize >> 8 ;
1664+ }
1665+
1666+ return true ;
1667+ }
1668+
1669+ bool d64::writeRecord (std::string_view filename, int recordNumber, const std::vector<uint8_t >& recordData) {
1670+ auto fileEntry = findFile (filename);
1671+ if (!fileEntry.has_value () || fileEntry.value ()->file_type .type != d64FileTypes::REL ) return false ;
1672+
1673+ int recordLength = fileEntry.value ()->recordLength ;
1674+ if (recordLength == 0 ) return false ;
1675+
1676+ if (recordData.size () != static_cast <size_t >(recordLength)) return false ;
1677+ if (recordNumber < 1 ) return false ;
1678+
1679+ int byteOffset = (recordNumber - 1 ) * recordLength;
1680+ int requiredBytes = byteOffset + recordLength;
1681+
1682+ if (!expandRelFile (filename, requiredBytes)) return false ;
1683+
1684+ int sectorIndex = byteOffset / 254 ;
1685+ int byteOffsetInSector = (byteOffset % 254 ) + 2 ;
1686+ int sideSectorIndex = sectorIndex / 120 ;
1687+ int pointerIndex = sectorIndex % 120 ;
1688+
1689+ trackSector sidePosition = fileEntry.value ()->side ;
1690+ sideSectorPtr side = nullptr ;
1691+
1692+ for (int i = 0 ; i <= sideSectorIndex; ++i) {
1693+ if (sidePosition.track == 0 ) return false ;
1694+ side = getSideSectorPtr (sidePosition.track , sidePosition.sector );
1695+ sidePosition = side->next ;
1696+ }
1697+
1698+ if (!side) return false ;
1699+ trackSector dataSectorPos = side->chain [pointerIndex];
1700+ if (dataSectorPos.track == 0 ) return false ;
1701+
1702+ auto dataSector = getSectorPtr (dataSectorPos.track , dataSectorPos.sector );
1703+
1704+ int bytesToWrite = recordLength;
1705+ int currentOffset = byteOffsetInSector;
1706+ int recordOffset = 0 ;
1707+
1708+ while (bytesToWrite > 0 ) {
1709+ int bytesAvailableInSector = 256 - currentOffset;
1710+ int bytesToCopy = std::min (bytesToWrite, bytesAvailableInSector);
1711+
1712+ std::copy_n (recordData.begin () + recordOffset, bytesToCopy, dataSector->data .begin () + currentOffset - 2 );
1713+
1714+ bytesToWrite -= bytesToCopy;
1715+ recordOffset += bytesToCopy;
1716+
1717+ if (bytesToWrite > 0 ) {
1718+ dataSectorPos = dataSector->next ;
1719+ if (dataSectorPos.track == 0 ) return false ;
1720+ dataSector = getSectorPtr (dataSectorPos.track , dataSectorPos.sector );
1721+ currentOffset = 2 ;
1722+ }
1723+ }
1724+
1725+ return true ;
1726+ }
1727+
1728+ bool d64::appendRecord (std::string_view filename, const std::vector<uint8_t >& recordData) {
1729+ int count = getRecordCount (filename);
1730+ return writeRecord (filename, count + 1 , recordData);
1731+ }
1732+
1733+ bool d64::deleteRecord (std::string_view filename, int recordNumber) {
1734+ int recordLength = getRecordSize (filename);
1735+ if (recordLength == 0 ) return false ;
1736+ std::vector<uint8_t > blankRecord (recordLength, 0x00 );
1737+ blankRecord[0 ] = 0xFF ;
1738+ return writeRecord (filename, recordNumber, blankRecord);
1739+ }
1740+
0 commit comments