mirror of
https://github.com/idolpx/libMeatloaf.git
synced 2025-12-06 04:38:49 -05:00
343 lines
9.9 KiB
C++
343 lines
9.9 KiB
C++
// .D64, .D41 - 1541 disk image format
|
|
//
|
|
// https://vice-emu.sourceforge.io/vice_17.html#SEC345
|
|
// https://ist.uwaterloo.ca/~schepers/formats/D64.TXT
|
|
// https://ist.uwaterloo.ca/~schepers/formats/GEOS.TXT
|
|
// https://www.lemon64.com/forum/viewtopic.php?t=70024&start=0 (File formats = Why is D64 not called D40/D41)
|
|
// - disucssion of disk id in sector missing from d64 file format is interesting
|
|
// https://www.c64-wiki.com/wiki/Disk_Image
|
|
// http://unusedino.de/ec64/technical3.html
|
|
// http://www.baltissen.org/newhtm/diskimag.htm
|
|
//
|
|
|
|
#ifndef MEATLOAF_MEDIA_D64
|
|
#define MEATLOAF_MEDIA_D64
|
|
|
|
#include "meat_io.h"
|
|
|
|
#include <map>
|
|
#include <bitset>
|
|
|
|
#include "string_utils.h"
|
|
#include "cbm_media.h"
|
|
|
|
|
|
/********************************************************
|
|
* Streams
|
|
********************************************************/
|
|
|
|
class D64IStream : public CBMImageStream {
|
|
|
|
protected:
|
|
|
|
struct BlockAllocationMap {
|
|
uint8_t track;
|
|
uint8_t sector;
|
|
uint8_t offset;
|
|
uint8_t start_track;
|
|
uint8_t end_track;
|
|
uint8_t byte_count;
|
|
};
|
|
|
|
struct Partition {
|
|
uint8_t header_track;
|
|
uint8_t header_sector;
|
|
uint8_t header_offset;
|
|
uint8_t directory_track;
|
|
uint8_t directory_sector;
|
|
uint8_t directory_offset;
|
|
std::vector<BlockAllocationMap> block_allocation_map;
|
|
};
|
|
|
|
struct Header {
|
|
char disk_name[16];
|
|
char unused[2];
|
|
char id_dos[5];
|
|
};
|
|
|
|
struct Entry {
|
|
uint8_t next_track;
|
|
uint8_t next_sector;
|
|
uint8_t file_type;
|
|
uint8_t start_track;
|
|
uint8_t start_sector;
|
|
char filename[16];
|
|
uint8_t rel_start_track; // Or GOES info block start track
|
|
uint8_t rel_start_sector; // Or GEOS info block start sector
|
|
uint8_t rel_record_length; // Or GEOS file structure (Sequential / VLIR file)
|
|
uint8_t geos_type; // $00 - Non-GEOS (normal C64 file)
|
|
uint8_t year;
|
|
uint8_t month;
|
|
uint8_t day;
|
|
uint8_t hour;
|
|
uint8_t minute;
|
|
uint16_t blocks;
|
|
};
|
|
|
|
public:
|
|
std::vector<Partition> partitions;
|
|
std::vector<uint8_t> sectorsPerTrack = { 17, 18, 19, 21 };
|
|
std::vector<uint8_t> interleave = { 3, 10 }; // Directory, File
|
|
|
|
uint8_t dos_version = 0x41;
|
|
std::string dos_rom = "dos1541";
|
|
std::string dos_name = "";
|
|
|
|
bool error_info = false;
|
|
std::string bam_message = "";
|
|
|
|
D64IStream(std::shared_ptr<MStream> is) : CBMImageStream(is)
|
|
{
|
|
// D64 Partition Info
|
|
std::vector<BlockAllocationMap> b = {
|
|
{
|
|
18, // track
|
|
0, // sector
|
|
0x04, // offset
|
|
1, // start_track
|
|
35, // end_track
|
|
4 // byte_count
|
|
}
|
|
};
|
|
|
|
Partition p = {
|
|
18, // track
|
|
0, // sector
|
|
0x90, // header_offset
|
|
18, // directory_track
|
|
1, // directory_sector
|
|
0x00, // directory_offset
|
|
b // block_allocation_map
|
|
};
|
|
partitions.clear();
|
|
partitions.push_back(p);
|
|
sectorsPerTrack = { 17, 18, 19, 21 };
|
|
|
|
uint32_t size = containerStream->size();
|
|
switch (size + media_header_size)
|
|
{
|
|
case 174848: // 35 tracks no errors
|
|
break;
|
|
|
|
case 175531: // 35 w/ errors
|
|
error_info = true;
|
|
break;
|
|
|
|
case 196608: // 40 tracks no errors
|
|
partitions[partition].block_allocation_map[0].end_track = 40;
|
|
break;
|
|
|
|
case 197376: // 40 w/ errors
|
|
partitions[partition].block_allocation_map[0].end_track = 40;
|
|
error_info = true;
|
|
break;
|
|
|
|
case 205312: // 42 tracks no errors
|
|
partitions[partition].block_allocation_map[0].end_track = 42;
|
|
break;
|
|
|
|
case 206114: // 42 w/ errors
|
|
partitions[partition].block_allocation_map[0].end_track = 42;
|
|
error_info = true;
|
|
break;
|
|
}
|
|
|
|
// Get DOS Version
|
|
|
|
// Extend BAM Info for DOLPHIN, SPEED, and ProLogic DOS
|
|
// The location of the extra BAM information in sector 18/0, for 40 track images,
|
|
// will be different depending on what standard the disks have been formatted with.
|
|
// SPEED DOS stores them from $C0 to $D3, DOLPHIN DOS stores them from $AC to $BF
|
|
// and PrologicDOS stored them right after the existing BAM entries from $90-A3.
|
|
// PrologicDOS also moves the disk label and ID forward from the standard location
|
|
// of $90 to $A4. 64COPY and Star Commander let you select from several different
|
|
// types of extended disk formats you want to create/work with.
|
|
|
|
// // DOLPHIN DOS
|
|
// partitions[0].block_allocation_map.push_back(
|
|
// {
|
|
// 18, // track
|
|
// 0, // sector
|
|
// 0xAC, // offset
|
|
// 36, // start_track
|
|
// 40, // end_track
|
|
// 4 // byte_count
|
|
// }
|
|
// );
|
|
|
|
// // SPEED DOS
|
|
// partitions[0].block_allocation_map.push_back(
|
|
// {
|
|
// 18, // track
|
|
// 0, // sector
|
|
// 0xC0, // offset
|
|
// 36, // start_track
|
|
// 40, // end_track
|
|
// 4 // byte_count
|
|
// }
|
|
// );
|
|
|
|
// // PrologicDOS
|
|
// partitions[0].block_allocation_map.push_back(
|
|
// {
|
|
// 18, // track
|
|
// 0, // sector
|
|
// 0x90, // offset
|
|
// 36, // start_track
|
|
// 40, // end_track
|
|
// 4 // byte_count
|
|
// }
|
|
// );
|
|
// partitions[0].header_offset = 0xA4;
|
|
|
|
//getBAMMessage();
|
|
|
|
};
|
|
|
|
// virtual std::unordered_map<std::string, std::string> info() override {
|
|
// return {
|
|
// {"System", "Commodore"},
|
|
// {"Format", "D64"},
|
|
// {"Media Type", "DISK"},
|
|
// {"Tracks", getTrackCount()},
|
|
// {"Sectors / Blocks", this.getSectorCount()},
|
|
// {"Sector / Block Size", std::string(block_size)},
|
|
// {"Error Info", (this.error_info) ? "Available" : "Not Available"},
|
|
// {"Write Protected", ""},
|
|
// {"DOS Format", this.getDiskFormat()}
|
|
// };
|
|
// };
|
|
|
|
uint16_t blocksFree() override;
|
|
|
|
uint8_t speedZone( uint8_t track) override
|
|
{
|
|
return (track < 18) + (track < 25) + (track < 31);
|
|
};
|
|
|
|
bool seekBlock( uint64_t index, uint8_t offset = 0 ) override;
|
|
bool seekSector( uint8_t track, uint8_t sector, uint8_t offset = 0 ) override;
|
|
bool seekSector( std::vector<uint8_t> trackSectorOffset ) override;
|
|
|
|
void seekHeader() override {
|
|
Debug_printv(".");
|
|
seekSector(
|
|
partitions[partition].header_track,
|
|
partitions[partition].header_sector,
|
|
partitions[partition].header_offset
|
|
);
|
|
containerStream->read((uint8_t*)&header, sizeof(header));
|
|
}
|
|
|
|
bool seekNextImageEntry() override {
|
|
return seekEntry( entry_index + 1 );
|
|
}
|
|
|
|
virtual bool seekPath(std::string path) override;
|
|
uint16_t readFile(uint8_t* buf, uint16_t size) override;
|
|
|
|
Header header; // Directory header data
|
|
Entry entry; // Directory entry data
|
|
|
|
uint8_t partition = 0;
|
|
uint64_t block = 0;
|
|
uint8_t track = 0;
|
|
uint8_t sector = 0;
|
|
uint16_t offset = 0;
|
|
uint64_t blocks_free = 0;
|
|
|
|
uint8_t next_track = 0;
|
|
uint8_t next_sector = 0;
|
|
uint8_t sector_offset = 0;
|
|
|
|
private:
|
|
void sendListing();
|
|
|
|
bool seekEntry( std::string filename ) override;
|
|
bool seekEntry( uint16_t index = 0 ) override;
|
|
|
|
|
|
std::string readBlock( uint8_t track, uint8_t sector );
|
|
bool writeBlock( uint8_t track, uint8_t sector, std::string data );
|
|
bool allocateBlock( uint8_t track, uint8_t sector );
|
|
bool deallocateBlock( uint8_t track, uint8_t sector );
|
|
|
|
// Disk
|
|
friend class D64File;
|
|
friend class D71File;
|
|
friend class D80File;
|
|
friend class D81File;
|
|
friend class D82File;
|
|
friend class D8BFile;
|
|
friend class DNPFile;
|
|
};
|
|
|
|
|
|
/********************************************************
|
|
* File implementations
|
|
********************************************************/
|
|
|
|
class D64File: public MFile {
|
|
public:
|
|
|
|
D64File(std::string path, bool is_dir = true): MFile(path)
|
|
{
|
|
isDir = is_dir;
|
|
|
|
media_image = name;
|
|
isPETSCII = true;
|
|
};
|
|
|
|
~D64File() {
|
|
// don't close the stream here! It will be used by shared ptr D64Util to keep reading image params
|
|
}
|
|
|
|
MStream* getDecodedStream(std::shared_ptr<MStream> containerIstream) override;
|
|
|
|
bool isDirectory() override;
|
|
bool rewindDirectory() override;
|
|
MFile* getNextFileInDir() override;
|
|
bool mkDir() override { return false; };
|
|
|
|
bool exists() override;
|
|
bool remove() override { return false; };
|
|
bool rename(std::string dest) override { return false; };
|
|
time_t getLastWrite() override;
|
|
time_t getCreationTime() override;
|
|
uint32_t size() override;
|
|
|
|
bool isDir = true;
|
|
bool dirIsOpen = false;
|
|
};
|
|
|
|
|
|
|
|
/********************************************************
|
|
* FS
|
|
********************************************************/
|
|
|
|
class D64FileSystem: public MFileSystem
|
|
{
|
|
public:
|
|
MFile* getFile(std::string path) override {
|
|
//Debug_printv("path[%s]", path.c_str());
|
|
return new D64File(path);
|
|
}
|
|
|
|
bool handles(std::string fileName) override {
|
|
//Serial.printf("handles w dnp %s %d\r\n", fileName.rfind(".dnp"), fileName.length()-4);
|
|
return byExtension(
|
|
{
|
|
".d64",
|
|
".d41"
|
|
},
|
|
fileName
|
|
);
|
|
}
|
|
|
|
D64FileSystem(): MFileSystem("d64") {};
|
|
};
|
|
|
|
|
|
#endif /* MEATLOAF_MEDIA_D64 */
|