mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-24 11:11:02 -07:00 
			
		
		
		
	
		
			
				
	
	
		
			355 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			355 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "lib/core/globals.h"
 | |
| #include "lib/vfs/vfs.h"
 | |
| #include "lib/config/config.pb.h"
 | |
| 
 | |
| /* Number of sectors on a 120kB disk. */
 | |
| static constexpr int SECTOR_COUNT = 468;
 | |
| 
 | |
| /* Start sector for the FAT (after the directory) */
 | |
| static constexpr int FAT_START_SECTOR = 8;
 | |
| 
 | |
| /* Start sector for data (after the FAT) */
 | |
| static constexpr int DATA_START_SECTOR = 14;
 | |
| 
 | |
| /* Size of a sector */
 | |
| static constexpr int SECTOR_SIZE = 256;
 | |
| 
 | |
| /* Number of dirents in a directory. */
 | |
| static constexpr int DIRECTORY_SIZE = 128;
 | |
| 
 | |
| /* Number of sectors in the FAT. */
 | |
| static constexpr int FAT_SECTORS = 4;
 | |
| 
 | |
| /* Number of sectors in a directory. */
 | |
| static constexpr int DIRECTORY_SECTORS = 8;
 | |
| 
 | |
| class Brother120Dirent : public Dirent
 | |
| {
 | |
| public:
 | |
|     Brother120Dirent(const Bytes& bytes)
 | |
|     {
 | |
|         ByteReader br(bytes);
 | |
|         filename = br.read(8);
 | |
| 
 | |
|         for (int i = 0; filename.size(); i++)
 | |
|         {
 | |
|             if (filename[i] == ' ')
 | |
|             {
 | |
|                 filename = filename.substr(0, i);
 | |
|                 break;
 | |
|             }
 | |
|             if ((filename[i] < 32) || (filename[i] > 126))
 | |
|                 throw BadFilesystemException();
 | |
|         }
 | |
| 
 | |
|         path = {filename};
 | |
| 
 | |
|         brotherType = br.read_8();
 | |
|         startSector = br.read_be16();
 | |
|         sectorLength = br.read_8();
 | |
|         length = sectorLength * SECTOR_SIZE;
 | |
|         file_type = TYPE_FILE;
 | |
|         mode = "";
 | |
| 
 | |
|         populateAttributes();
 | |
|     }
 | |
| 
 | |
|     Brother120Dirent(const std::string& filename,
 | |
|         int brotherType,
 | |
|         uint16_t startSector,
 | |
|         uint8_t sectorLength):
 | |
|         brotherType(brotherType),
 | |
|         startSector(startSector),
 | |
|         sectorLength(sectorLength)
 | |
|     {
 | |
|         this->filename = filename;
 | |
|         path = {filename};
 | |
|         length = sectorLength * SECTOR_SIZE;
 | |
|         file_type = TYPE_FILE;
 | |
|         mode = "";
 | |
| 
 | |
|         populateAttributes();
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     void populateAttributes()
 | |
|     {
 | |
|         attributes[Filesystem::FILENAME] = filename;
 | |
|         attributes[Filesystem::LENGTH] = std::to_string(length);
 | |
|         attributes[Filesystem::FILE_TYPE] = "file";
 | |
|         attributes[Filesystem::MODE] = mode;
 | |
|         attributes["brother120.start_sector"] = std::to_string(startSector);
 | |
|         attributes["brother120.type"] = std::to_string(brotherType);
 | |
|     }
 | |
| 
 | |
| public:
 | |
|     int brotherType;
 | |
|     uint32_t startSector;
 | |
|     uint32_t sectorLength;
 | |
| };
 | |
| 
 | |
| class BrotherDirectory
 | |
| {
 | |
| public:
 | |
|     BrotherDirectory(Filesystem* fs)
 | |
|     {
 | |
|         /* Read directory. */
 | |
| 
 | |
|         for (int block = 0; block < DIRECTORY_SECTORS; block++)
 | |
|         {
 | |
|             auto bytes = fs->getLogicalSector(block);
 | |
|             for (int d = 0; d < SECTOR_SIZE / 16; d++)
 | |
|             {
 | |
|                 Bytes buffer = bytes.slice(d * 16, 16);
 | |
|                 if (buffer[0] & 0x80)
 | |
|                     continue;
 | |
| 
 | |
|                 auto de = std::make_shared<Brother120Dirent>(buffer);
 | |
|                 usedSectors += de->sectorLength;
 | |
|                 dirents.push_back(de);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /* Read FAT. */
 | |
| 
 | |
|         Bytes bytes = fs->getLogicalSector(FAT_START_SECTOR, FAT_SECTORS);
 | |
|         ByteReader br(bytes);
 | |
| 
 | |
|         fat.push_back(0xffff);
 | |
|         for (int sector = 1; sector != SECTOR_COUNT; sector++)
 | |
|             fat.push_back(br.read_be16());
 | |
|     }
 | |
| 
 | |
|     void write(Filesystem* fs)
 | |
|     {
 | |
|         Bytes bytes;
 | |
|         ByteWriter bw(bytes);
 | |
| 
 | |
|         /* Write the directory. */
 | |
| 
 | |
|         if (dirents.size() > DIRECTORY_SIZE)
 | |
|             throw DiskFullException("directory full");
 | |
|         int i = 0;
 | |
|         for (const auto& de : dirents)
 | |
|         {
 | |
|             bw.append(Bytes(de->filename + "        ").slice(0, 8));
 | |
|             bw.write_8(de->brotherType);
 | |
|             bw.write_be16(de->startSector);
 | |
|             bw.write_8(de->sectorLength);
 | |
|             bw.write_le32(0);
 | |
|             i++;
 | |
|         }
 | |
|         while (i < DIRECTORY_SIZE)
 | |
|         {
 | |
|             bw.write_8(0xf0);
 | |
|             bw.append(Bytes(15));
 | |
|             i++;
 | |
|         }
 | |
| 
 | |
|         /* Write the FAT. */
 | |
| 
 | |
|         for (int i = 1; i != SECTOR_COUNT; i++)
 | |
|             bw.write_be16(fat[i]);
 | |
| 
 | |
|         fs->putLogicalSector(0, bytes);
 | |
|     }
 | |
| 
 | |
|     std::shared_ptr<Brother120Dirent> findFile(const Path& path)
 | |
|     {
 | |
|         if (path.size() != 1)
 | |
|             throw BadPathException();
 | |
| 
 | |
|         for (auto& dirent : dirents)
 | |
|         {
 | |
|             if (dirent->filename == path[0])
 | |
|                 return dirent;
 | |
|         }
 | |
| 
 | |
|         throw FileNotFoundException();
 | |
|     }
 | |
| 
 | |
|     void deleteFile(const Path& path)
 | |
|     {
 | |
|         if (path.size() != 1)
 | |
|             throw BadPathException();
 | |
| 
 | |
|         for (auto it = dirents.begin(); it != dirents.end(); it++)
 | |
|         {
 | |
|             auto& dirent = *it;
 | |
|             if (dirent->filename == path[0])
 | |
|             {
 | |
|                 int sector = dirent->startSector;
 | |
| 
 | |
|                 while ((sector != 0) && (sector != 0xffff))
 | |
|                 {
 | |
|                     int nextSector = fat.at(sector);
 | |
|                     freeSector(sector);
 | |
|                     sector = nextSector;
 | |
|                 }
 | |
| 
 | |
|                 dirents.erase(it);
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         throw FileNotFoundException();
 | |
|     }
 | |
| 
 | |
|     uint16_t allocateSector()
 | |
|     {
 | |
|         for (int i = 0; i < fat.size(); i++)
 | |
|             if (fat[i] == 0)
 | |
|             {
 | |
|                 fat[i] = 0xffff;
 | |
|                 usedSectors++;
 | |
|                 return i;
 | |
|             }
 | |
|         throw DiskFullException();
 | |
|     }
 | |
| 
 | |
|     void freeSector(uint16_t i)
 | |
|     {
 | |
|         if (fat[i])
 | |
|         {
 | |
|             fat[i] = 0;
 | |
|             usedSectors--;
 | |
|         }
 | |
|     }
 | |
| 
 | |
| public:
 | |
|     std::vector<uint16_t> fat;
 | |
|     std::vector<std::shared_ptr<Brother120Dirent>> dirents;
 | |
|     uint32_t usedSectors = 0;
 | |
| };
 | |
| 
 | |
| class Brother120Filesystem : public Filesystem
 | |
| {
 | |
| public:
 | |
|     Brother120Filesystem(const Brother120FsProto& config,
 | |
|         std::shared_ptr<SectorInterface> sectors):
 | |
|         Filesystem(sectors),
 | |
|         _config(config)
 | |
|     {
 | |
|     }
 | |
| 
 | |
|     uint32_t capabilities() const override
 | |
|     {
 | |
|         return OP_GETFSDATA | OP_LIST | OP_GETFILE | OP_PUTFILE | OP_GETDIRENT |
 | |
|                OP_DELETE;
 | |
|     }
 | |
| 
 | |
|     std::map<std::string, std::string> getMetadata() override
 | |
|     {
 | |
|         BrotherDirectory dir(this);
 | |
| 
 | |
|         std::map<std::string, std::string> attributes;
 | |
|         attributes[VOLUME_NAME] = "";
 | |
|         attributes[TOTAL_BLOCKS] = std::to_string(getLogicalSectorCount());
 | |
|         attributes[USED_BLOCKS] = std::to_string(dir.usedSectors);
 | |
|         attributes[BLOCK_SIZE] = std::to_string(SECTOR_SIZE);
 | |
|         return attributes;
 | |
|     }
 | |
| 
 | |
|     FilesystemStatus check() override
 | |
|     {
 | |
|         return FS_OK;
 | |
|     }
 | |
| 
 | |
|     std::vector<std::shared_ptr<Dirent>> list(const Path& path) override
 | |
|     {
 | |
|         if (!path.empty())
 | |
|             throw FileNotFoundException();
 | |
| 
 | |
|         BrotherDirectory dir(this);
 | |
| 
 | |
|         std::vector<std::shared_ptr<Dirent>> result;
 | |
|         for (auto& dirent : dir.dirents)
 | |
|             result.push_back(dirent);
 | |
| 
 | |
|         return result;
 | |
|     }
 | |
| 
 | |
|     Bytes getFile(const Path& path) override
 | |
|     {
 | |
|         BrotherDirectory dir(this);
 | |
|         auto dirent = dir.findFile(path);
 | |
|         int sector = dirent->startSector;
 | |
| 
 | |
|         Bytes data;
 | |
|         ByteWriter bw(data);
 | |
|         while ((sector != 0) && (sector != 0xffff))
 | |
|         {
 | |
|             bw += getLogicalSector(sector - 1);
 | |
|             sector = dir.fat.at(sector);
 | |
|         }
 | |
| 
 | |
|         return data;
 | |
|     }
 | |
| 
 | |
|     void putFile(const Path& path, const Bytes& data) override
 | |
|     {
 | |
|         BrotherDirectory dir(this);
 | |
| 
 | |
|         try
 | |
|         {
 | |
|             dir.findFile(path);
 | |
|             throw CannotWriteException("file exists");
 | |
|         }
 | |
|         catch (const FileNotFoundException& e)
 | |
|         {
 | |
|         }
 | |
| 
 | |
|         auto& filename = path.back();
 | |
|         if (filename.size() > 8)
 | |
|             throw CannotWriteException(
 | |
|                 "filename too long (eight characters maximum)");
 | |
| 
 | |
|         int sectorLength = (data.size() + SECTOR_SIZE - 1) / SECTOR_SIZE;
 | |
|         if (sectorLength > 0xff)
 | |
|             throw CannotWriteException("file is too big (64kB is the maximum)");
 | |
| 
 | |
|         ByteReader br(data);
 | |
| 
 | |
|         int firstSector = 0xffff;
 | |
|         int previousSector = 0;
 | |
|         while (!br.eof())
 | |
|         {
 | |
|             int currentSector = dir.allocateSector();
 | |
|             if (previousSector)
 | |
|                 dir.fat[previousSector] = currentSector;
 | |
| 
 | |
|             putLogicalSector(currentSector - 1, br.read(SECTOR_SIZE));
 | |
|             if (firstSector == 0xffff)
 | |
|                 firstSector = currentSector;
 | |
|             previousSector = currentSector;
 | |
|         }
 | |
| 
 | |
|         auto dirent = std::make_shared<Brother120Dirent>(
 | |
|             filename, 0, firstSector, sectorLength);
 | |
|         dir.dirents.push_back(dirent);
 | |
|         dir.write(this);
 | |
|     }
 | |
| 
 | |
|     void deleteFile(const Path& path) override
 | |
|     {
 | |
|         BrotherDirectory dir(this);
 | |
|         dir.deleteFile(path);
 | |
|         dir.write(this);
 | |
|     }
 | |
| 
 | |
|     std::shared_ptr<Dirent> getDirent(const Path& path) override
 | |
|     {
 | |
|         BrotherDirectory dir(this);
 | |
|         return dir.findFile(path);
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     const Brother120FsProto& _config;
 | |
| };
 | |
| 
 | |
| std::unique_ptr<Filesystem> Filesystem::createBrother120Filesystem(
 | |
|     const FilesystemProto& config, std::shared_ptr<SectorInterface> sectors)
 | |
| {
 | |
|     return std::make_unique<Brother120Filesystem>(config.brother120(), sectors);
 | |
| }
 |