Skip to content

Commit 44007c8

Browse files
authored
Merge pull request #5 from vschwaberow/geos_and_rel_support
Geos and rel support
2 parents f62283a + 0f976f6 commit 44007c8

9 files changed

Lines changed: 924 additions & 35 deletions

File tree

CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
99
add_subdirectory(unittests)
1010

1111
# Add library
12-
add_library(d64lib d64.cpp d64.h d64_types.h)
12+
add_library(d64lib d64.cpp d64.h d64_types.h geos.cpp geos.h)
1313

1414
# Export the include directory
1515
target_include_directories(d64lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
1616
target_link_directories(d64lib PUBLIC ${CMAKE_CURRENT_LINK_DIR})
1717

1818
# Install rules (optional for packaging)
1919
install(TARGETS d64lib DESTINATION lib)
20-
install(FILES d64.h DESTINATION include)
20+
install(FILES d64.h d64_types.h geos.h DESTINATION include)

d64.cpp

Lines changed: 294 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,22 +1385,24 @@ std::vector<directoryEntry> d64::directory()
13851385
/// <returns>true if valid</returns>
13861386
bool 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>
14151417
std::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

Comments
 (0)