mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-31 11:17:01 -07:00 
			
		
		
		
	
		
			
				
	
	
		
			501 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			501 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "globals.h"
 | 
						|
#include "bytes.h"
 | 
						|
#include "fmt/format.h"
 | 
						|
#include "utils.h"
 | 
						|
#include <fstream>
 | 
						|
#include "fnmatch.h"
 | 
						|
 | 
						|
/* Number of sectors on a 120kB disk. */
 | 
						|
static constexpr int SECTOR_COUNT = 468;
 | 
						|
 | 
						|
/* Start sector for data (after the directory */
 | 
						|
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;
 | 
						|
 | 
						|
struct Dirent
 | 
						|
{
 | 
						|
    std::string filename;
 | 
						|
    int type;
 | 
						|
    int startSector;
 | 
						|
    int sectorCount;
 | 
						|
};
 | 
						|
 | 
						|
static std::fstream file;
 | 
						|
static std::map<std::string, std::unique_ptr<Dirent>> directory;
 | 
						|
static std::map<uint16_t, uint16_t> allocationTable;
 | 
						|
 | 
						|
void syntax()
 | 
						|
{
 | 
						|
    std::cout << "Syntax: brother120tool <image> [<filenames...>]\n"
 | 
						|
                 "        brother120tool --create <image> <filenames...>\n"
 | 
						|
                 "If you specify a filename, it's extracted into the current "
 | 
						|
                 "directory.\n"
 | 
						|
                 "Wildcards are allowed. If you don't, the directory is listed "
 | 
						|
                 "instead.\n";
 | 
						|
    exit(0);
 | 
						|
}
 | 
						|
 | 
						|
void readDirectory()
 | 
						|
{
 | 
						|
    for (int i = 0; i < DIRECTORY_SIZE; i++)
 | 
						|
    {
 | 
						|
        file.seekg(i * 16, std::ifstream::beg);
 | 
						|
 | 
						|
        Bytes buffer(16);
 | 
						|
        file.read((char*)&buffer[0], buffer.size());
 | 
						|
        if (buffer[0] == 0xf0)
 | 
						|
            continue;
 | 
						|
 | 
						|
        ByteReader br(buffer);
 | 
						|
        std::string filename = br.read(8);
 | 
						|
        filename = filename.substr(0, filename.find(" "));
 | 
						|
 | 
						|
        std::unique_ptr<Dirent> dirent(new Dirent);
 | 
						|
        dirent->filename = filename;
 | 
						|
        dirent->type = br.read_8();
 | 
						|
        dirent->startSector = br.read_be16();
 | 
						|
        dirent->sectorCount = br.read_8();
 | 
						|
        directory[filename] = std::move(dirent);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void writeDirectory()
 | 
						|
{
 | 
						|
    Bytes buffer(2048);
 | 
						|
    ByteWriter bw(buffer);
 | 
						|
 | 
						|
    int count = 0;
 | 
						|
    for (const auto& it : directory)
 | 
						|
    {
 | 
						|
        const auto& dirent = it.second;
 | 
						|
 | 
						|
        if (count == DIRECTORY_SIZE)
 | 
						|
            Error() << "too many files on disk";
 | 
						|
 | 
						|
        bw.append(dirent->filename);
 | 
						|
        for (int i = dirent->filename.size(); i < 8; i++)
 | 
						|
            bw.write_8(' ');
 | 
						|
 | 
						|
        bw.write_8(dirent->type);
 | 
						|
        bw.write_be16(dirent->startSector);
 | 
						|
        bw.write_8(dirent->sectorCount);
 | 
						|
        bw.write_be32(0); /* unknown */
 | 
						|
        count++;
 | 
						|
    }
 | 
						|
 | 
						|
    static const Bytes padding(15);
 | 
						|
    while (count < DIRECTORY_SIZE)
 | 
						|
    {
 | 
						|
        bw.write_8(0xf0);
 | 
						|
        bw.append(padding);
 | 
						|
        count++;
 | 
						|
    }
 | 
						|
 | 
						|
    file.seekp(0, std::ifstream::beg);
 | 
						|
    buffer.writeTo(file);
 | 
						|
}
 | 
						|
 | 
						|
void writeBootSector(const Dirent& dirent, uint16_t checksum)
 | 
						|
{
 | 
						|
	uint8_t sslo = dirent.startSector & 0xff;
 | 
						|
	uint8_t sshi = dirent.startSector >> 8;
 | 
						|
	uint8_t scnt = dirent.sectorCount;
 | 
						|
	uint8_t end = 0x70 + scnt;
 | 
						|
	uint8_t cklo = checksum & 0xff;
 | 
						|
	uint8_t ckhi = checksum >> 8;
 | 
						|
 | 
						|
	uint8_t machineCode[] =
 | 
						|
	{
 | 
						|
		/* 6000 */ 0x55,             /* magic number?         */
 | 
						|
		/* 6001 */ 0xf3,             /* di                    */
 | 
						|
		/* 6002 */ 0x31, 0x00, 0x00, /* ld sp, $0000          */
 | 
						|
		/* 6005 */ 0x3e, 0x00,       /* ld a, $00             */
 | 
						|
		/* 6007 */ 0xed, 0x39, 0x34, /* out0 ($34), a         */
 | 
						|
		/* 600a */ 0x3e, 0x0f,       /* ld a, $0f             */
 | 
						|
		/* 600c */ 0xed, 0x39, 0x0a, /* out0 ($0a), a         */
 | 
						|
		/* 600f */ 0x3e, 0x00,       /* ld a,$00              */
 | 
						|
		/* 6011 */ 0xed, 0x39, 0xd8, /* out0 ($d8),a          */
 | 
						|
		/* 6014 */ 0x3e, 0xe7,       /* ld a,$e7              */
 | 
						|
		/* 6016 */ 0xed, 0x39, 0x3a, /* out0 ($3a),a          */
 | 
						|
		/* 6019 */ 0x3e, 0x16,       /* ld a,$16              */
 | 
						|
		/* 601b */ 0xed, 0x39, 0x38, /* out0 ($38),a          */
 | 
						|
		/* 601e */ 0x3e, 0x20,       /* ld a,$20              */
 | 
						|
		/* 6020 */ 0xed, 0x39, 0x39, /* out0 ($39),a          */
 | 
						|
		/* 6023 */ 0x01, sslo, sshi, /* ld bc, start sector   */
 | 
						|
		/* 6026 */ 0x21, 0x00, 0xf8, /* ld hl,$f800           */
 | 
						|
		/* 6029 */ 0x1e, scnt,       /* ld e, sector count    */
 | 
						|
		/* 602b */ 0x16, 0x70,       /* ld d,$70              */
 | 
						|
		/* 602d */ 0xc5,             /* push bc               */
 | 
						|
		/* 602e */ 0xd5,             /* push de               */
 | 
						|
		/* 602f */ 0xe5,             /* push hl               */
 | 
						|
		/* 6030 */ 0x3e, 0x06,       /* ld a,$06              */
 | 
						|
		/* 6032 */ 0xef,             /* rst $28               */
 | 
						|
		/* 6033 */ 0xda, 0xd3, 0x60, /* jp c,$60d3            */
 | 
						|
		/* 6036 */ 0xe1,             /* pop hl                */
 | 
						|
		/* 6037 */ 0xd1,             /* pop de                */
 | 
						|
		/* 6038 */ 0xc1,             /* pop bc                */
 | 
						|
		/* 6039 */ 0x3e, 0x00,       /* ld a,$00              */
 | 
						|
		/* 603b */ 0xed, 0x39, 0x20, /* out0 ($20),a          */
 | 
						|
		/* 603e */ 0x3e, 0x58,       /* ld a,$58              */
 | 
						|
		/* 6040 */ 0xed, 0x39, 0x21, /* out0 ($21),a          */
 | 
						|
		/* 6043 */ 0x3e, 0x02,       /* ld a,$02              */
 | 
						|
		/* 6045 */ 0xed, 0x39, 0x22, /* out0 ($22),a          */
 | 
						|
		/* 6048 */ 0x3e, 0x00,       /* ld a,$00              */
 | 
						|
		/* 604a */ 0xed, 0x39, 0x23, /* out0 ($23),a          */
 | 
						|
		/* 604d */ 0x7a,             /* ld a,d                */
 | 
						|
		/* 604e */ 0xed, 0x39, 0x24, /* out0 ($24),a          */
 | 
						|
		/* 6051 */ 0x3e, 0x02,       /* ld a,$02              */
 | 
						|
		/* 6053 */ 0xed, 0x39, 0x25, /* out0 ($25),a          */
 | 
						|
		/* 6056 */ 0x3e, 0x00,       /* ld a,$00              */
 | 
						|
		/* 6058 */ 0xed, 0x39, 0x26, /* out0 ($26),a          */
 | 
						|
		/* 605b */ 0x3e, 0x01,       /* ld a,$01              */
 | 
						|
		/* 605d */ 0xed, 0x39, 0x27, /* out0 ($27),a          */
 | 
						|
		/* 6060 */ 0x3e, 0x02,       /* ld a,$02              */
 | 
						|
		/* 6062 */ 0xed, 0x39, 0x31, /* out0 ($31),a          */
 | 
						|
		/* 6065 */ 0x3e, 0x40,       /* ld a,$40              */
 | 
						|
		/* 6067 */ 0xed, 0x39, 0x30, /* out0 ($30),a          */
 | 
						|
		/* 606a */ 0x03,             /* inc bc                */
 | 
						|
		/* 606b */ 0x14,             /* inc d                 */
 | 
						|
		/* 606c */ 0x1d,             /* dec e                 */
 | 
						|
		/* 606d */ 0x7b,             /* ld a,e                */
 | 
						|
		/* 606e */ 0xfe, 0x00,       /* cp $00                */
 | 
						|
		/* 6070 */ 0x20, 0xbb,       /* jr nz,$602d           */
 | 
						|
		/* 6072 */ 0x3e, 0x02,       /* ld a,$02              */
 | 
						|
		/* 6074 */ 0xef,             /* rst $28               */
 | 
						|
		/* 6075 */ 0x3e, 0x20,       /* ld a,$20              */
 | 
						|
		/* 6077 */ 0xed, 0x39, 0x38, /* out0 ($38),a          */
 | 
						|
		/* 607a */ 0x3e, 0x0c,       /* ld a,$0c              */
 | 
						|
		/* 607c */ 0xed, 0x39, 0x39, /* out0 ($39),a          */
 | 
						|
		/* 607f */ 0x3e, 0x64,       /* ld a,$64              */
 | 
						|
		/* 6081 */ 0xed, 0x39, 0x3a, /* out0 ($3a),a          */
 | 
						|
		/* 6084 */ 0x3e, 0x0f,       /* ld a,$0f              */
 | 
						|
		/* 6086 */ 0xed, 0x39, 0x0a, /* out0 ($0a),a          */
 | 
						|
 | 
						|
		/* checksum routine */
 | 
						|
		/* 6089 */ 0x01, 0x00, 0x70, /* ld bc,$7000           */
 | 
						|
		/* 608c */ 0x11, 0x00, 0x00, /* ld de,$0000           */
 | 
						|
		/* 608f */ 0x21, 0x00, 0x00, /* ld hl,$0000           */
 | 
						|
		/* 6092 */ 0x0a,             /* ld a,(bc)             */
 | 
						|
		/* 6093 */ 0x5f,             /* ld e,a                */
 | 
						|
		/* 6094 */ 0x19,             /* add hl,de             */
 | 
						|
		/* 6095 */ 0x03,             /* inc bc                */
 | 
						|
		/* 6096 */ 0x79,             /* ld a,c                */
 | 
						|
		/* 6097 */ 0xfe, 0x00,       /* cp $00                */
 | 
						|
		/* 6099 */ 0x20, 0xf7,       /* jr nz,$6092           */
 | 
						|
		/* 609b */ 0x78,             /* ld a,b                */
 | 
						|
		/* 609c */ 0xfe, end,        /* cp end page           */
 | 
						|
		/* 609e */ 0x20, 0xf2,       /* jr nz,$6092           */
 | 
						|
		/* 60a0 */ 0x11, 0xf3, 0x60, /* ld de,$60f3           */
 | 
						|
		/* 60a3 */ 0x1a,             /* ld a,(de)             */
 | 
						|
		/* 60a4 */ 0xbd,             /* cp l                  */
 | 
						|
		/* 60a5 */ 0x20, 0x2f,       /* jr nz,$60d6           */
 | 
						|
		/* 60a7 */ 0x13,             /* inc de                */
 | 
						|
		/* 60a8 */ 0x1a,             /* ld a,(de)             */
 | 
						|
		/* 60a9 */ 0xbc,             /* cp h                  */
 | 
						|
		/* 60aa */ 0x20, 0x2a,       /* jr nz,$60d6           */
 | 
						|
 | 
						|
		/* reset and execute */
 | 
						|
		/* 60ac */ 0x3e, 0xff,       /* ld a,$ff              */
 | 
						|
		/* 60ae */ 0xed, 0x39, 0x88, /* out0 ($88),a          */
 | 
						|
		/* 60b1 */ 0x01, 0xff, 0x0f, /* ld bc,$0fff           */
 | 
						|
		/* 60b4 */ 0x21, 0x00, 0x40, /* ld hl,$4000           */
 | 
						|
		/* 60b7 */ 0x11, 0x01, 0x40, /* ld de,$4001           */
 | 
						|
		/* 60ba */ 0x36, 0x00,       /* ld (hl),$00           */
 | 
						|
		/* 60bc */ 0xed, 0xb0,       /* ldir                  */
 | 
						|
		/* 60be */ 0x01, 0xff, 0x0f, /* ld bc,$0fff           */
 | 
						|
		/* 60c1 */ 0x21, 0x00, 0x50, /* ld hl,$5000           */
 | 
						|
		/* 60c4 */ 0x11, 0x01, 0x50, /* ld de,$5001           */
 | 
						|
		/* 60c7 */ 0x36, 0x20,       /* ld (hl),$20           */
 | 
						|
		/* 60c9 */ 0xed, 0xb0,       /* ldir                  */
 | 
						|
		/* 60cb */ 0x3e, 0xfe,       /* ld a,$fe              */
 | 
						|
		/* 60cd */ 0xed, 0x39, 0x88, /* out0 ($88),a          */
 | 
						|
		/* 60d0 */ 0xc3, 0x00, 0x70, /* jp $7000              */
 | 
						|
 | 
						|
		/* 60d3 */ 0xe1,             /* pop hl                */
 | 
						|
		/* 60d4 */ 0xd1,             /* pop de                */
 | 
						|
		/* 60d5 */ 0xc1,             /* pop bc                */
 | 
						|
		/* 60d6 */ 0x01, 0x00, 0x00, /* ld bc,$0000           */
 | 
						|
		/* 60d9 */ 0x0b,             /* dec bc                */
 | 
						|
		/* 60da */ 0x3e, 0xfe,       /* ld a,$fe              */
 | 
						|
		/* 60dc */ 0xed, 0x39, 0x90, /* out0 ($90),a          */
 | 
						|
		/* 60df */ 0x78,             /* ld a,b                */
 | 
						|
		/* 60e0 */ 0xb1,             /* or c                  */
 | 
						|
		/* 60e1 */ 0x20, 0xf6,       /* jr nz,$60d9           */
 | 
						|
		/* 60e3 */ 0x31, 0x00, 0x00, /* ld sp,$0000           */
 | 
						|
		/* 60e6 */ 0x3e, 0xff,       /* ld a,$ff              */
 | 
						|
		/* 60e8 */ 0xed, 0x39, 0x90, /* out0 ($90),a          */
 | 
						|
		/* 60eb */ 0x3e, 0x0f,       /* ld a,$0f              */
 | 
						|
		/* 60ed */ 0xed, 0x39, 0x0a, /* out0 ($0a),a          */
 | 
						|
		/* 60f0 */ 0xc3, 0x00, 0x00, /* jp $0000              */
 | 
						|
		/* 60f3 */ cklo, ckhi,       /* checksum              */
 | 
						|
	};
 | 
						|
 | 
						|
    file.seekp(0xc00, std::ifstream::beg);
 | 
						|
	file.write((char*) machineCode, sizeof(machineCode));
 | 
						|
}
 | 
						|
 | 
						|
static bool isValidFile(const Dirent& dirent)
 | 
						|
{
 | 
						|
    return (dirent.filename[0] & 0x80) == 0;
 | 
						|
}
 | 
						|
 | 
						|
void readAllocationTable()
 | 
						|
{
 | 
						|
    for (int sector = 1; sector != SECTOR_COUNT; sector++)
 | 
						|
    {
 | 
						|
        file.seekg((sector - 1) * 2 + 0x800, std::ifstream::beg);
 | 
						|
        Bytes buffer(2);
 | 
						|
        file.read((char*)&buffer[0], buffer.size());
 | 
						|
 | 
						|
        uint16_t nextSector = buffer.reader().read_be16();
 | 
						|
        allocationTable[sector] = nextSector;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void writeAllocationTable()
 | 
						|
{
 | 
						|
    Bytes buffer(SECTOR_COUNT * 2);
 | 
						|
    ByteWriter bw(buffer);
 | 
						|
 | 
						|
    for (int sector = 1; sector < (DATA_START_SECTOR - 2); sector++)
 | 
						|
        bw.write_le16(sector + 1);
 | 
						|
    bw.write_le16(0xffff);
 | 
						|
    bw.write_le16(0xffff);
 | 
						|
    for (int sector = DATA_START_SECTOR; sector != SECTOR_COUNT; sector++)
 | 
						|
        bw.write_be16(allocationTable[sector]);
 | 
						|
 | 
						|
    file.seekp(0x800, std::ifstream::beg);
 | 
						|
    buffer.writeTo(file);
 | 
						|
}
 | 
						|
 | 
						|
uint16_t allocateSector()
 | 
						|
{
 | 
						|
    for (int sector = DATA_START_SECTOR; sector != SECTOR_COUNT; sector++)
 | 
						|
    {
 | 
						|
        if (allocationTable[sector] == 0)
 | 
						|
        {
 | 
						|
            allocationTable[sector] = 0xffff;
 | 
						|
            return sector;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    Error() << "unable to allocate sector --- disk full";
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
void checkConsistency()
 | 
						|
{
 | 
						|
    /* Verify that we more-or-less understand the format by fscking the disk. */
 | 
						|
 | 
						|
    std::vector<bool> bitmap(640);
 | 
						|
    for (const auto& i : directory)
 | 
						|
    {
 | 
						|
        const Dirent& dirent = *i.second;
 | 
						|
        if (!isValidFile(dirent))
 | 
						|
            continue;
 | 
						|
 | 
						|
        int count = 0;
 | 
						|
        uint16_t sector = dirent.startSector;
 | 
						|
        while ((sector != 0xffff) && (sector != 0))
 | 
						|
        {
 | 
						|
            if (bitmap[sector])
 | 
						|
                std::cout << fmt::format(
 | 
						|
                    "warning: sector {} appears to be multiply used\n", sector);
 | 
						|
            bitmap[sector] = true;
 | 
						|
            sector = allocationTable[sector];
 | 
						|
            count++;
 | 
						|
        }
 | 
						|
 | 
						|
        if (count != dirent.sectorCount)
 | 
						|
            std::cout << fmt::format(
 | 
						|
                "Warning: file '{}' claims to be {} sectors long but its chain "
 | 
						|
                "is {}\n",
 | 
						|
                dirent.filename,
 | 
						|
                dirent.sectorCount,
 | 
						|
                count);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void listDirectory()
 | 
						|
{
 | 
						|
    for (const auto& i : directory)
 | 
						|
    {
 | 
						|
        const Dirent& dirent = *i.second;
 | 
						|
        std::cout << fmt::format("{:9} {:6.2f}kB type {}: ",
 | 
						|
            dirent.filename,
 | 
						|
            (double)dirent.sectorCount / 4.0,
 | 
						|
            dirent.type);
 | 
						|
 | 
						|
        if (isValidFile(dirent))
 | 
						|
            std::cout << fmt::format("{} sectors starting at sector {}",
 | 
						|
                dirent.sectorCount,
 | 
						|
                dirent.startSector);
 | 
						|
        else
 | 
						|
            std::cout << "DELETED";
 | 
						|
 | 
						|
        std::cout << std::endl;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void insertFile(const std::string& filename)
 | 
						|
{
 | 
						|
    auto leafname = getLeafname(filename);
 | 
						|
    if (leafname.size() > 8)
 | 
						|
        Error() << "filename too long";
 | 
						|
    std::cout << fmt::format("Inserting '{}'\n", leafname);
 | 
						|
 | 
						|
    std::ifstream inputFile(filename, std::ios::in | std::ios::binary);
 | 
						|
    if (!inputFile)
 | 
						|
        Error() << fmt::format(
 | 
						|
            "unable to open input file: {}", strerror(errno));
 | 
						|
 | 
						|
    if (directory.find(leafname) != directory.end())
 | 
						|
        Error() << fmt::format("duplicate filename: {}", leafname);
 | 
						|
 | 
						|
    auto dirent = std::make_unique<Dirent>();
 | 
						|
    dirent->filename = leafname;
 | 
						|
    dirent->type = (leafname.find('*') != std::string::npos);
 | 
						|
    dirent->startSector = 0xffff;
 | 
						|
    dirent->sectorCount = 0;
 | 
						|
 | 
						|
    uint16_t lastSector = 0xffff;
 | 
						|
	uint16_t checksum = 0;
 | 
						|
    while (!inputFile.eof())
 | 
						|
    {
 | 
						|
        uint8_t buffer[SECTOR_SIZE] = {};
 | 
						|
        inputFile.read((char*) buffer, sizeof(buffer));
 | 
						|
		for (int i=0; i<inputFile.gcount(); i++)
 | 
						|
			checksum += buffer[i];
 | 
						|
        if (inputFile.gcount() == 0)
 | 
						|
            break;
 | 
						|
        if (inputFile.bad())
 | 
						|
            Error() << fmt::format("I/O error on read: {}", strerror(errno));
 | 
						|
 | 
						|
        uint16_t thisSector = allocateSector();
 | 
						|
        if (lastSector == 0xffff)
 | 
						|
            dirent->startSector = thisSector;
 | 
						|
        else
 | 
						|
            allocationTable[lastSector] = thisSector;
 | 
						|
        dirent->sectorCount++;
 | 
						|
 | 
						|
        file.seekp((thisSector - 1) * 0x100, std::ifstream::beg);
 | 
						|
        file.write((char*) buffer, sizeof(buffer));
 | 
						|
        if (file.bad())
 | 
						|
            Error() << fmt::format("I/O error on write: {}", strerror(errno));
 | 
						|
 | 
						|
        lastSector = thisSector;
 | 
						|
    }
 | 
						|
 | 
						|
	if (leafname == "*boot")
 | 
						|
	{
 | 
						|
		std::cout << fmt::format(
 | 
						|
			"Writing boot sector with checksum 0x{:04x}\n", checksum);
 | 
						|
		writeBootSector(*dirent, checksum);
 | 
						|
	}
 | 
						|
 | 
						|
    directory[leafname] = std::move(dirent);
 | 
						|
}
 | 
						|
 | 
						|
void extractFile(const std::string& pattern)
 | 
						|
{
 | 
						|
    for (const auto& i : directory)
 | 
						|
    {
 | 
						|
        const Dirent& dirent = *i.second;
 | 
						|
        if ((dirent.type == 0xf0) || (dirent.type == 0xe5))
 | 
						|
            continue;
 | 
						|
 | 
						|
        if (fnmatch(pattern.c_str(), dirent.filename.c_str(), 0))
 | 
						|
            continue;
 | 
						|
 | 
						|
        std::cout << fmt::format("Extracting '{}'\n", dirent.filename);
 | 
						|
 | 
						|
        std::ofstream outputFile(dirent.filename,
 | 
						|
            std::ios::out | std::ios::binary | std::ios::trunc);
 | 
						|
        if (!outputFile)
 | 
						|
            Error() << fmt::format(
 | 
						|
                "unable to open output file: {}", strerror(errno));
 | 
						|
 | 
						|
        uint16_t sector = dirent.startSector;
 | 
						|
        while ((sector != 0) && (sector != 0xffff))
 | 
						|
        {
 | 
						|
            uint8_t buffer[256];
 | 
						|
            file.seekg((sector - 1) * 0x100, std::ifstream::beg);
 | 
						|
            if (!file.read((char*)buffer, sizeof(buffer)))
 | 
						|
                Error() << fmt::format(
 | 
						|
                    "I/O error on read: {}", strerror(errno));
 | 
						|
            if (!outputFile.write((const char*)buffer, sizeof(buffer)))
 | 
						|
                Error() << fmt::format(
 | 
						|
                    "I/O error on write: {}", strerror(errno));
 | 
						|
 | 
						|
            sector = allocationTable[sector];
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void doCreate(int argc, const char* argv[])
 | 
						|
{
 | 
						|
    if (argc < 3)
 | 
						|
        syntax();
 | 
						|
 | 
						|
    file.open(argv[1], std::ios::out | std::ios::binary | std::ios::trunc);
 | 
						|
    if (!file.is_open())
 | 
						|
        Error() << fmt::format("cannot open output file '{}'", argv[1]);
 | 
						|
 | 
						|
    file.seekp(SECTOR_COUNT * SECTOR_SIZE - 1, std::ifstream::beg);
 | 
						|
    file.put(0);
 | 
						|
 | 
						|
    for (int i = 2; i < argc; i++)
 | 
						|
        insertFile(argv[i]);
 | 
						|
 | 
						|
    writeDirectory();
 | 
						|
    writeAllocationTable();
 | 
						|
    checkConsistency();
 | 
						|
 | 
						|
    file.close();
 | 
						|
}
 | 
						|
 | 
						|
static void doExtract(int argc, const char* argv[])
 | 
						|
{
 | 
						|
    if (argc < 2)
 | 
						|
        syntax();
 | 
						|
 | 
						|
    file.open(argv[1], std::ios::in | std::ios::binary);
 | 
						|
    if (!file.is_open())
 | 
						|
        Error() << fmt::format("cannot open input file '{}'", argv[1]);
 | 
						|
 | 
						|
    readDirectory();
 | 
						|
    readAllocationTable();
 | 
						|
    checkConsistency();
 | 
						|
 | 
						|
    if (argc == 2)
 | 
						|
        listDirectory();
 | 
						|
    else
 | 
						|
    {
 | 
						|
        for (int i = 2; i < argc; i++)
 | 
						|
            extractFile(argv[i]);
 | 
						|
    }
 | 
						|
 | 
						|
    file.close();
 | 
						|
}
 | 
						|
 | 
						|
int main(int argc, const char* argv[])
 | 
						|
{
 | 
						|
    try
 | 
						|
    {
 | 
						|
        if ((argc > 1) && (strcmp(argv[1], "--create") == 0))
 | 
						|
            doCreate(argc - 1, argv + 1);
 | 
						|
        else
 | 
						|
            doExtract(argc, argv);
 | 
						|
    }
 | 
						|
    catch (const ErrorException& e)
 | 
						|
    {
 | 
						|
        e.print();
 | 
						|
        exit(1);
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 |