{ 
	Driver->storage->fat32 - fat32 filesystem driver
	
	@author(Aaron Hance <ah@aaronhance.me>)
}

unit FAT32;

interface

uses
    console,
    storagemanagement,
    util, terminal,
    lmemorymanager,
    strings,
    lists,
    tracer,
    serial,
    rtc;

type 

    TBootRecord = bitpacked record
        jmp2boot        : ubit24;
        OEMName         : array[0..7] of char;
        sectorSize      : uint16;
        spc             : uint8;
        rsvSectors      : uint16;
        numFats         : uint8;
        numDirEnt       : uint16;
        numSectors      : uint16;
        mediaDescp      : uint8;
        sectorsPerFat   : uint16;
        sectorsPerTrack : uint16;
        heads           : uint16;
        hiddenSectors   : uint32;
        manySectors     : uint32;
        FATSize       : uint32;
        flags         : uint16;
        signature     : uint8;
        FATVersion    : uint16;
        rootCluster   : uint32;
        FSInfoCluster : uint16;
        backupCluster : uint16;
        reserved0     : array[0..11] of uint8;
        driveNumber   : uint8;
        reserved1     : uint8;
        bsignature     : uint8;// = $28;
        volumeID      : uint32;
        volumeLabel   : array[0..10] of uint8;
        identString   : array[0..7] of char;// = 'FAT32 ';
    end;
    PBootRecord = ^TBootRecord;

    TDirectory = packed record
        fileName      : array[0..7] of char;
        fileExtension : array[0..2] of char;
        attributes    : uint8;
        reserved0     : uint8;
        timeFine      : uint8;
        time          : uint16;
        date          : uint16;
        accessTime    : uint16;
        clusterHigh   : uint16;
        modifiedTime  : uint16;
        modifiedDate  : uint16;
        clusterLow    : uint16;
        byteSize      : uint32;
    end;
    PDirectory = ^TDirectory;

    TFilesystemInfo = record
        leadSignature   : uint32;
        reserved0       : array[0..479] of uint8;
        structSignature : uint32;
        freeSectors     : uint32;
        nextFreeSector  : uint32;
        reserved1       : array[0..11] of uint8;
        trailSignature  : uint32; 
    end;

    TFatVolumeInfo = record
        sectorsPerCluster : uint8; // must be power of 2 and mult by sectorsize to max 32k
    end;
    PFatVolumeInfo = ^TFatVolumeInfo;

var
    filesystem : TFilesystem;

procedure init;
procedure create_volume(disk : PStorage_Device; sectors : uint32; start : uint32; config : puint32);
procedure detect_volumes(disk : PStorage_Device);
//function writeDirectory(volume : PStorage_volume; directory : pchar; attributes : uint32) : uint8; // need to handle parent table cluster overflow, need to take attributes
//function readDirectory(volume : PStorage_volume; directory : pchar; listPtr : PLinkedListBase) : uint8; //returns: 0 = success, 1 = dir not exsist, 2 = not directory, 3 = error


implementation

procedure STOS(str : PChar);
var
    i : uint32;

begin
    for i:=0 to StringSize(str)-1 do begin
        serial.send(COM1, uint8(str[i]), 100);
    end;
    serial.send(COM1, 13, 100);
end;

function load(ptr : void) : boolean;
begin
    console.outputln('FAT32', 'LOADED.')
end;

function cleanString(str : pchar; status : puint32) : byteArray8;
var
    i : uint32;
    ii: uint32;
begin
    //puint32(cleanString):= puint32(kalloc(sizeof(10)));
    //memset(uint32(@cleanString), 0, 10);
    push_trace('cleanstring()');
    for i:=0 to 7 do begin
        if str[i] = char(0) then begin
            for ii:=i to 7 do begin
                cleanString[ii]:= ' ';
            end;
            break;
        end else begin
            // if (str[i] = '/') or (str[i] = ',') then begin
            //     status:= 3;
            // end;
            cleanString[i]:= str[i];
        end;
    end;

end;    

function cleanStringCha(str : pchar) : pchar;
var
    i : uint32;
    ii: uint32;
begin
    cleanStringCha:= pchar(kalloc(10));
    memset(uint32(cleanstringcha), 0, 10);
        push_trace('cleanstringcha');
    for i:=0 to 7 do begin
            push_trace('cleanstringcha1');

        if str[i] = char(0) then begin
                push_trace('cleanstringcha1.1');

            for ii:=i to 7 do begin
                cleanStringCha[ii]:= ' ';
            end;
            break;
        end else begin
                        push_trace('cleanstringcha1.2');
            cleanstringcha[i]:= str[i];
                            push_trace('cleanstringcha1.3');

        end;
    end;
            push_trace('cleanstringcha2');

end;

function readBootRecord(volume : PStorage_volume) : PBootRecord; // need write functions for boot record!
var
    buffer : puint32;
begin
    buffer:= puint32(kalloc(512));
    memset(uint32(buffer), 0, 512);
    volume^.device^.readcallback(volume^.device, volume^.sectorStart + 1, 1, buffer);
    readBootRecord:= PBootRecord(buffer);
end;

//TODO fat starts after reserved secotrs

function readFat(volume : PStorage_volume; cluster : uint32; bootRecord : PBootRecord) : uint32;
var
    buffer              : puint32;
    fatEntriesPerSector : uint32;
    sectorLocation      : uint32;
    dataStart : uint32;
begin
    push_trace('fat32.readFat');
    buffer:= puint32(kalloc(bootRecord^.sectorsize));
    memset(uint32(buffer), 0, bootRecord^.sectorsize);
    fatEntriesPerSector:= bootRecord^.sectorsize div 4;
    sectorLocation:= cluster div fatEntriesPerSector;
    dataStart:= (volume^.sectorStart + 1 + bootRecord^.rsvSectors);

    volume^.device^.readcallback(volume^.device, datastart + sectorLocation, 1, buffer); 
    readFat:= buffer[cluster - (sectorLocation * fatEntriesPerSector)];

    kfree(buffer);
end;

procedure writeFat(volume : PStorage_volume; cluster : uint32; value : uint32; bootRecord : PBootRecord);
var
    buffer              : puint32;
    fatEntriesPerSector : uint32;
    sectorLocation      : uint32;
    dataStart           : uint32;
begin
    push_trace('fat32.WriteFat');
    buffer:= puint32(kalloc(bootRecord^.sectorsize));
    memset(uint32(buffer), 0, bootRecord^.sectorsize);
    fatEntriesPerSector:= bootRecord^.sectorsize div 4;
    sectorLocation:= cluster div fatEntriesPerSector;
    dataStart:= (volume^.sectorStart + 1 + bootRecord^.rsvSectors);

    volume^.device^.readcallback(volume^.device, dataStart + sectorLocation, 1, buffer);
    buffer[cluster - (sectorLocation * fatEntriesPerSector)]:= value;
    volume^.device^.writecallback(volume^.device, dataStart + sectorLocation, 1, buffer);

    kfree(buffer);
end;

function getFatChain(volume : PStorage_volume; cluster : uint32; bootRecord : PBootRecord) : PLinkedListBase;
var
    currentCluster      : uint32;
    currentClusterValue : uint32;
    clusters            : PLinkedListBase;
    dirElm              : puint32;
begin
    push_trace('fat32.getFatChain');
    clusters:= LL_New(sizeof(uint32));
    currentCluster:= cluster;
    currentClusterValue:= cluster;

    while true do begin
        currentClusterValue:= readFat(volume, currentClusterValue, bootRecord);

        if currentClusterValue = $FFFFFFF7 then begin
            break;
        end else if currentClusterValue = $FFFFFFF8 then begin
            dirElm:= LL_add(clusters);
            dirElm^:= currentCluster;
            break;
        end else if currentClusterValue = 0 then begin
            break;
        end else begin
            dirElm:= LL_add(clusters);
            dirElm^:= currentCluster;
        end;

        currentCluster+=1;
    end;

    getFatChain:= clusters;
end;

//TODO improve with FSINFO
function findFreeClusters(volume : PStorage_volume; amount : uint32; bootRecord : PBootRecord) : PLinkedListBase;
var
    i                   : uint32 = 2;
    currentClusterValue : uint32;
    currentAmount       : uint32 = 0;
    clusters            : PLinkedListBase;
    dirElm              : puint32;
begin
    push_trace('fat32.findFreeClusters');
    clusters := LL_New(8);
    dirElm:= kalloc(sizeof(TDirectory_Entry)); //TODO FREE

    while true do begin

        if currentAmount = amount then break;

        currentClusterValue:= readFat(volume, i, bootRecord);

        if currentClusterValue = 0 then begin
            dirElm:= LL_add(clusters);
            dirElm^:= i;
            currentAmount+=1;
        end;

        i+=1;
    end;

    findFreeClusters:= clusters;

end;

//TODO add optional attributes flag to refine what i return
function getDirEntries(volume : PStorage_volume; cluster : uint32; bootRecord : PBootRecord) : PLinkedListBase;
var
    buffer : puint32;
    bufferI : puint32;
    clusters : PLinkedListBase;
    directories : PLinkedListBase;
    i : uint32 = 0;
    datastart : uint32;
    sectorLocation : uint32;
    dirElm : puint32;
begin
    push_trace('fat32.getDirEntries');
    directories:= LL_New(sizeof(TDirectory));

    clusters:= PLinkedListBase(getFatChain(volume, cluster, bootRecord));
    buffer:= puint32(kalloc( (bootRecord^.sectorSize * bootRecord^.spc) * LL_size(clusters) ));
    memset(uint32(buffer), 0, (bootRecord^.sectorSize * bootRecord^.spc) * LL_size(clusters) );

    dataStart:= volume^.sectorStart + 1 + bootRecord^.rsvSectors + bootRecord^.FATSize;

    for i:=0 to LL_size(clusters) - 1 do begin
        sectorLocation:= bootRecord^.spc * (i + cluster);
        bufferI:= @buffer[i * (bootRecord^.spc * bootRecord^.sectorSize)];
        volume^.device^.readcallback(volume^.device, datastart + sectorLocation, bootRecord^.spc, bufferI); //datastart + spc(i + cluster)
    end;

    i:=0;
    while true do begin
        if PDirectory(buffer)[i].fileName[0] = char(0) then break;

        dirElm:= LL_Add(directories);
        PDirectory(dirElm)^:= PDirectory(buffer)[i];
        i+=1;
    end;

    getDirEntries:= directories; //get last .
    LL_Free(clusters);
    kfree(buffer);
end;

function compareByteArray8(str1 : byteArray8; str2 : byteArray8) : boolean;
var
    i : uint32;
begin
    push_trace('fat32.compareArray');
    compareByteArray8:= true;
    for i:=0 to 7 do begin
        if str1[i] <> str2[i] then begin
            compareByteArray8:= false;
            break;
        end;
    end;
end;

function fat2GenericEntries(list : PLinkedListBase) : PLinkedListBase;
var
    i     : uint32;
    entry : PDirectory_Entry;
    dir   : PDirectory;
    dirElm: puint32;
begin
    push_trace('fat32.fat2GenericEntries');
    puint32(entry) := kalloc(sizeof(TDirectory_Entry));
    dirElm:= kalloc(sizeof(TDirectory_Entry));
    fat2GenericEntries:= LL_New(sizeof(TDirectory_Entry));

    if LL_size(list) > 0 then begin //TODO

        for i:= 0 to LL_Size(list) - 1 do begin
            dir := PDirectory(LL_get(list, i));
            entry^.fileName:= pchar(dir^.fileName);
            entry^.extension:= pchar(dir^.fileExtension);

            if dir^.attributes = $10 then begin
                entry^.entryType:= TDirectory_Entry_Type.directoryEntry;
            end else begin //TODO add mount type
                entry^.entryType:= TDirectory_Entry_Type.fileEntry;
            end;

            //add to list
            dirElm:= LL_add(fat2GenericEntries);
            PDirectory_Entry(dirElm)^:= entry^;
        end;
    end;

    kfree(puint32(list));
    kfree(puint32(entry));
    kfree(dirElm);
end;

//need to find out why having multiple dir stings isn't working, maybe the ls command? did I fix this?
function readDirectory(volume : PStorage_volume; directory : pchar; statusOut : puint32) : PLinkedListBase; //statusout: 0 = success, 1 = dir not exsist, 2 = not directory, 3 = invalid name, 4= already exists
var
    bootRecord       : PBootRecord;
    directoryStrings : PLinkedListBase;
    directories      : PLinkedListBase;
    cluster          : uint32;
    i                : uint32;
    ii               : uint32 = 0;
    dirEntry         : PDirectory;
    status : puint32;
begin
    status:= puint32(kalloc(sizeof(uint32)));
    push_trace('fat32.readDirectory');
    status^:= 0;
    bootRecord:= readBootRecord(volume);
    directoryStrings:= LL_fromString(directory, '/');
    directories:= getDirEntries(volume, bootRecord^.rootCluster, bootRecord);

    if LL_size(directoryStrings) > 0 then begin
        for i:=0 to (LL_Size(directoryStrings) ) do begin /// maybe -1 will work
            ii:=0;

            while true do begin
                if ii > LL_Size(directories) - 1 then begin
                    status^:= 1;
                    break; 
                end;
                dirEntry:= PDirectory(LL_Get(directories, ii));

                if compareByteArray8( dirEntry^.fileName, cleanString( pchar(puint32(LL_Get(directoryStrings, i))^), status)) then begin
                    cluster:= uint32(dirEntry^.clusterLow);
                    cluster:= uint32(cluster) or uint32(dirEntry^.clusterHigh shl 16);
                    break;
                end;
                ii+=1;
            end;

            if status^ <> 0 then break;
            
            LL_Free(directories); 
            directories:= getDirEntries(volume, cluster, bootRecord);

            if i = LL_Size(directoryStrings) - 1 then break;
        end;
    end else begin
        while true do begin
            if ii > LL_Size(directories) - 1 then break;
            dirEntry:= PDirectory(LL_Get(directories, ii));
            ii+=1;
        end;
    end;

    readDirectory:= directories;

    statusOut:= status;
    LL_Free(directoryStrings);
    kfree(puint32(bootRecord));
end;

function readDirectoryGen(volume : PStorage_volume; directory : pchar; status : puint32) : PLinkedListBase; //returns: 0 = success, 1 = dir not exsist, 2 = not directory, 3 = invalid name, 4= already exists
begin
    readDirectoryGen:= fat2GenericEntries(readDirectory(volume, directory, status));
end;

//need to allow for setting file extension
function writeDirectory(volume : PStorage_volume; directory : pchar; dirName : pchar; attributes : uint32; statusOut : puint32; fileExtension : pchar) : uint32; // need to handle parent table cluster overflow, need to take attributes
var
    directories     : PLinkedListBase;
    parentDirectory : PDirectory;
    parentCluster   : uint32;
    clusters        : PLinkedListBase;
    cluster         : uint32;
    bootRecord      : PBootRecord;
    buffer          : puint32;
    bufferPointer   : PDirectory;
    dataStart       : uint32;
    EntriesPerSector : uint32;
    sectorLocation  : uint32;
    dataOffset      : uint32;
    i : uint32;

    thisArray     : byteArray8 = ('.',' ',' ',' ',' ',' ',' ',' ');
    parentArray   : byteArray8 = ('.','.',' ',' ',' ',' ',' ',' ');
    status : puint32;
begin
    push_trace('fat32.writeDirectory');
    status:= puint32(kalloc(sizeof(uint32)));
    status^:= 0;

    directories:= readDirectory(volume, directory, status); 

    if(LL_size(directories) > 1) then begin
        for i:=0 to LL_Size(directories) - 1 do begin
            if compareByteArray8( pchar(PDirectory(LL_get(directories, i))^.fileName), cleanString( dirName , status)) then begin
                status^:= 4;
            end;
        end;
    end;

    bootRecord:= readBootRecord(volume);
    datastart:= volume^.sectorStart + 1 + bootRecord^.FATSize + bootRecord^.rsvSectors;

    if status^ = 0 then begin

        parentDirectory:= PDirectory(LL_Get(directories, 0));
        parentCluster:= uint32(parentDirectory^.clusterlow) or uint32(parentDirectory^.clusterhigh shl 16);
        //TODO check if this cluster is full, if so allocate new cluster

        clusters:= findFreeClusters(volume, 1, bootRecord); 
        cluster:= uint32(LL_Get(clusters, 0)^);
        LL_Free(clusters);
        buffer:= puint32(kalloc(bootRecord^.sectorSize));

        if attributes = $10 then begin // if directory

            memset(uint32(buffer), 0, bootRecord^.sectorSize);

            bufferPointer:= @PDirectory(buffer)[0];
            bufferPointer^.fileName:= thisArray; //TODO implement time
            bufferPointer^.attributes:= attributes;
            bufferPointer^.clusterLow:= cluster;
            bufferPointer^.clusterHigh:= uint16((cluster shr 16) and $0000FFFF);
            
            bufferPointer:= @PDirectory(buffer)[1];
            bufferPointer^.fileName:= parentArray; //TODO implement time
            bufferPointer^.attributes:= attributes;
            bufferPointer^.clusterLow:= parentCluster;
            bufferPointer^.clusterHigh:= uint16((parentCluster shr 16) and $0000FFFF);

            //write to disk
            volume^.device^.writecallback(volume^.device, dataStart + (cluster * bootRecord^.spc), 1, buffer);

            //write fat
            writeFat(volume, cluster, $FFFFFFF8, bootRecord);
        end;
        memset(uint32(buffer), 0, bootRecord^.sectorSize);
        //calculate write cluster using directories and parentCluster
        sectorLocation:= LL_size(directories) * sizeof(TDirectory) div bootRecord^.sectorSize;
        sectorLocation:= sectorLocation + (parentCluster * bootRecord^.spc);

        //dataOffset:= datastart + ( (LL_size(directories) * sizeof(PDirectory)) - (sizeUsed * bootRecord^.sectorSize));
        volume^.device^.readcallback(volume^.device, dataStart + sectorLocation, 1, buffer);

        //construct my dir entry
        bufferPointer:= @PDirectory(buffer)[LL_size(directories)];
        bufferPointer^.fileName:= cleanString(dirName, status);
        bufferPointer^.attributes:= attributes;
        if attributes = 0 then bufferPointer^.fileExtension:= fileExtension;
        bufferPointer^.clusterLow:= cluster;
        bufferPointer^.clusterHigh:= uint16((cluster shr 16) and $0000FFFF);

        writeDirectory:= cluster;

        //write to disk
        volume^.device^.writecallback(volume^.device, dataStart + sectorLocation, 1, buffer);
        kfree(buffer);
    end;

    statusOut^:= status^;
    kfree(puint32(bootRecord));
    //LL_Free(directories); // page fault on free, possible memory leak now?
    push_trace('writedirectory.end');

end;

procedure writeDirectoryGen(volume : PStorage_volume; directory : pchar; dirName : pchar; attributes : uint32; statusOut : puint32); // need to handle parent table cluster overflow, need to take attributes
begin
    writeDirectory(volume, directory, dirName, attributes, statusOut, '');
end;

procedure writeFile(volume : PStorage_volume; directory : pchar; entry : PDirectory_Entry; byteCount : uint32; buffer : puint32; statusOut : puint32);
var
    bootRecord : PBootRecord;
    directories : PLinkedListBase;
    clusters : PLinkedListBase;
    startCluster: uint32;
    device : PStorage_Device;
    dir : PDirectory;
    exists : boolean = false;
    sectorCount : uint32;
    clusterCount : uint32;
    clusterDifference : uint32;
    dataStart : uint32;
    iterations : uint32;
    bufferPointer : puint32;
    dataPosition : uint32;

    i : uint32;
    status : puint32;
begin
    push_trace('fat32.writefile');
    status:= kalloc(4);
    bootRecord:= readBootRecord(volume);
    device:= volume^.device;
    directories:= readDirectory(volume, directory, statusOut);
    sectorCount:= (byteCount div bootRecord^.sectorSize) + 1;
    datastart:= volume^.sectorStart + 1 + bootRecord^.FATSize + bootRecord^.rsvSectors;


    for i:=0 to LL_size(directories) - 1 do begin
        dir:= PDirectory(LL_get(directories, i));
        if (dir^.fileName = entry^.fileName) and (dir^.fileExtension = entry^.extension) then begin
            exists:= true; 
            break;
        end;
    end;

    push_trace('writefile.1');

    if exists then begin //saving to existing file
        startCluster:= uint32(dir^.clusterlow) or uint32(dir^.clusterhigh shl 16);
        clusters:= getFatChain(volume, startCluster, bootRecord); //check no clusters and check if needs to be more or less, add/remove clusters
        clusterCount := LL_size(clusters);

        if (clusterCount * bootRecord^.spc) > sectorCount then begin //shrink
            clusterDifference:= clusterCount - (sectorCount div bootRecord^.spc);
            for i:= (clusterCount - clusterDifference) + 1 to clusterCount do begin //free unused clusters
                writeFat(volume, startCluster + i, 0, bootRecord);
            end;
            writeFat(volume, startCluster + (clusterCount - clusterDifference), $FFFFFFF8, bootRecord); // add new cluster terminator
            //change last entry to terminator.
        end else if (clusterCount * bootRecord^.spc) < sectorCount then begin //expand
            clusterDifference:= (sectorCount div bootRecord^.spc) - clusterCount;
            LL_Free(clusters);
            clusters:= findFreeClusters(volume, clusterDifference, bootRecord);
            for i:= clusterCount to clusterCount + clusterDifference - 1 do begin
                writeFat(volume, startCluster + i, startCluster + i + 1, bootRecord);
            end;
                writeFat(volume, startcluster + clusterCount + clusterDifference, $FFFFFFF8, bootRecord);
        end else begin //nothing
            clusterDifference:= 0;
        end;

    end else begin
        push_trace('writefile.1.2.1');

        startCluster:= writeDirectory(volume, directory, entry^.fileName, 0, status, entry^.extension);
        clusterDifference:= (byteCount div bootRecord^.sectorsize) div 4; //-1 is that needed?
            push_trace('writefile.1.2.2');

        for i:= startcluster to startCluster + clusterDifference - 1 do begin
            writeFat(volume, i, i + 1, bootRecord);
        end;
                push_trace('writefile.1.2.3');

        writeFat(volume, startcluster + clusterDifference, $FFFFFFF8, bootRecord);
        //setup fat chain 
        console.writestringWND('clust diff: ', getTerminalHWND());
        console.writeintlnWND(clusterDifference, getTerminalHWND());
        console.writestringWND('startclust: ', getTerminalHWND());
        console.writeintlnWND(startcluster, getTerminalHWND());
    end;

    push_trace('writefile.2');

    iterations:= (bytecount div bootRecord^.sectorSize) div bootRecord^.spc; //no of clusters

    for i:=0 to iterations do begin
        console.writestringlnWND('writting to file', getTerminalHWND());
        dataPosition:= i * uint32(bootRecord^.sectorsize * 4); //needs to be bytes / 4
        bufferPointer:= @buffer[dataPosition div 4]; //todo change to puint8
        volume^.device^.writecallback(volume^.device, dataStart + (startCluster * bootRecord^.spc) + (i * 4), 1, bufferPointer); //i * 4 needs to be changed, TODO fix fucking IDE driver, it suks
        volume^.device^.writecallback(volume^.device, dataStart + (startCluster * bootRecord^.spc) + (i * 4) + 1, 1, @bufferPointer[512 div 4]); 
        volume^.device^.writecallback(volume^.device, dataStart + (startCluster * bootRecord^.spc) + (i * 4) + 2, 1, @bufferPointer[1024 div 4]); 
        volume^.device^.writecallback(volume^.device, dataStart + (startCluster * bootRecord^.spc) + (i * 4) + 3, 1, @bufferPointer[1536 div 4]); 
    end;

    kfree(puint32(bootRecord));
end;

function readFile(volume : PStorage_Volume; directory : pchar; fileName : pchar; fileExtension : pchar; buffer : puint32; bytecount : puint32) : uint32;
var
    bootRecord  : PBootRecord;
    dirs        : PLinkedListBase;
    dir         : PDirectory;
    readbuffer  : puint32;
    data        : puint32;
    statusOut   : puint32;
    i           : uint32;
    exists      : boolean = false;
    cluster     : uint32;
    clusters    : PLinkedListBase;
    noClusters  : uint32;
    dataStart   : uint32;
    cleanFileName : byteArray8;
    otherCleanFileName : byteArray8;
    tempdir : PDirectory;
begin
    push_trace('readfile');
    bootRecord := readBootRecord(volume);
    dirs := readDirectory(volume, directory, statusOut);
    datastart:= volume^.sectorStart + 1 + bootRecord^.FATSize + bootRecord^.rsvSectors;

    if LL_size(dirs) > 0 then begin
        cleanFileName := cleanString(filename, statusOut);

        for i:=0 to LL_Size(dirs) -1 do begin
            tempdir := PDirectory(LL_get(dirs, i));
            otherCleanFileName := cleanString(tempdir^.filename, statusout); //TODO clean extension

            if compareByteArray8(cleanFileName, otherCleanFileName) then begin
                dir:=tempdir;
                exists := true;
                statusOut^ := 0;
            end;
        end;
    end;


    if exists = false then begin
        statusOut^ := 1; //TODO use right error codes
        exit(1);
    end else begin
        cluster := uint32(dir^.clusterlow) or uint32(dir^.clusterhigh shl 16);
        clusters := getFatChain(volume, cluster, bootRecord);
        noClusters := LL_size(clusters);

        data := puint32(kalloc(noClusters * bootRecord^.spc * bootRecord^.sectorSize + bootRecord^.sectorSize));
        if data = puint32(0) then begin
            push_trace('UNABLE TO ALLOCATE MEMORY');
        end;
        memset(uint32(data), 0, noClusters * bootRecord^.spc * bootRecord^.sectorSize + bootRecord^.sectorSize);

        readbuffer := puint32(kalloc(bootRecord^.sectorSize * 2));
        if readbuffer = puint32(0) then begin
            push_trace('UNABLE TO ALLOCATE MEMORY');
        end;
        memset(uint32(readbuffer), 0, bootRecord^.sectorSize * 2);

        for i:=0 to noClusters * bootRecord^.spc do begin
            volume^.device^.readcallback(volume^.device, dataStart + ((cluster * bootRecord^.spc)+ i), 1, readbuffer);
            memcpy(uint32(readbuffer), uint32(@data[i*bootRecord^.sectorSize div 4]), bootRecord^.sectorSize);
        end;

        kfree(readbuffer);
        buffer^ := uint32(data);
        bytecount^ := noClusters * bootRecord^.spc * bootRecord^.sectorSize + bootRecord^.sectorSize;//maybe need to spc
        readFile:= statusOut^;
    end;

end;

// function checkExists(volume : PStorage_Volume; directory : pchar; fileName : pchar; fileExtension : pchar; entry : PDirectory_Entry) : uint32;
// var
//     bootRecord : PBootRecord;
//     directories : PLinkedListBase;
//     dir : PDirectory;
//     genDir : PDirectory_Entry;
//     data : puint32;
//     statusOut : puint32;
//     i : uint32;
//     exists : boolean = false;
// begin
//     bootRecord := readBootRecord(volume);
//     directories := readDirectory(volume, directory, statusOut);
//     datastart:= volume^.sectorStart + 1 + bootRecord^.FATSize + bootRecord^.rsvSectors;

//     for i:=0 to LL_Size(directories) -1 do begin
//         dir:= PDirectory(LL_Get(directories, i));
//         if (dir^.fileName = entry^.fileName) and (dir^.fileExtension = entry^.extension) then begin
//             exists:= true;
//             break;
//         end;
//     end;

//     PDirectory_Entry := PDirectory_Entry(kalloc(20));

//     PDirectory_Entry^.fileName := pchar(@dir^.fileName);
//     PDirectory_Entry^.extension := pchar(@dir^.fileExtension);

// end;

//TODO check directory commands for errors with a clean disk

procedure create_volume(disk : PStorage_Device; sectors : uint32; start : uint32; config : puint32);
var
    buffer     : puint32;
    bt: puint32;
    zeroBuffer : puint32;
    bootRecord : PBootRecord;
    dataStart  : uint32;
    fatStart   : uint32;
    FATSize    : uint32;
    i          : uint32 = 0;

    asuroArray    : byteArray8 = ('A','S','U','R','O',' ','V','1');
    fatArray      : byteArray8 = ('F','A','T','3','2',' ',' ',' ');
    thisArray     : byteArray8 = ('.',' ',' ',' ',' ',' ',' ',' ');
    parentArray   : byteArray8 = ('.','.',' ',' ',' ',' ',' ',' ');

    asuroFileArray     : byteArray8 = ('A','S','U','R','O',' ',' ',' ');
    mountFileArray     : byteArray8 = ('M','O','U','N','T',' ',' ',' ');
    programFileArray   : byteArray8 = ('P','R','O','G','R','A','M','S');
    rootCluster   : uint32 = 1;

begin //maybe increase buffer size by one?
    push_trace('fat32.create_volume()');

    // zeroBuffer:= puint32(kalloc( disk^.sectorSize * 4 ));
    // memset(uint32(zeroBuffer), 0, disk^.sectorSize * 4);

    // console.writeintlnWND(disk^.sectorsize, getTerminalHWND());
    // console.writehexlnWND(zeroBuffer[(disk^.sectorSize) - 1], getTerminalHWND());
    // console.writehexlnWND(uint32(zeroBuffer), getTerminalHWND());
    // console.redrawWindows();

    // while true do begin
    //      if i > sectors then break;
    //      disk^.writecallback(disk, 1 + i, 1, zeroBuffer);

    //      i+=1;
    // end;
    // kfree(zeroBuffer);

    //fat32 structure
    (* BootRecord            *)
    (* reserved sectors      *)
    (* File Allocation Table *)
    (* Data Area             *)
    
    // buffer:= puint32(kalloc(sizeof(TBootRecord)));
    // memset(uint32(buffer), 0, sizeof(TBootRecord));
    // bootRecord:= PBootRecord(buffer);

    buffer:= puint32(kalloc(disk^.sectorSize+512));
    memset(uint32(buffer), 0, disk^.sectorSize);

    bootRecord:= PBootRecord(buffer);

    FATSize:= ((sectors div config^) * 4) div disk^.sectorsize;

    bootRecord^.jmp2boot        := $0; //TODO impliment boot jump
    bootRecord^.OEMName         := asuroArray;
    bootRecord^.sectorSize      := disk^.sectorsize;
    bootRecord^.spc             := config^;
    bootRecord^.rsvSectors      := 32; //32 is standard
    bootRecord^.numFats         := 1;
    bootRecord^.mediaDescp      := $F8;
    bootRecord^.hiddenSectors   := start;
    bootRecord^.manySectors     := sectors;
    bootRecord^.FATSize         := FATSize;
    bootRecord^.rootCluster     := rootCluster;
    bootRecord^.FSInfoCluster   := 0;
    bootRecord^.driveNumber     := $80;
    bootRecord^.volumeID        := 62; //+ puint32(@rtc.getDateTime())^;
    bootRecord^.bsignature      := $29;
    bootRecord^.identString     := fatArray;


    console.writestringWND('spc', getTerminalHWND());
    console.writeintlnWND(config^, getTerminalHWND());
    console.writestringWND('sectors', getTerminalHWND());
    console.writeintlnWND(sectors, getTerminalHWND());
    console.writestringWND('sectorsize', getTerminalHWND());
    console.writeintlnWND(disk^.sectorsize, getTerminalHWND());

    console.redrawWindows();

    bt:= puint32(kalloc(512));

    bt[1] := 1;
    bt[2] := 1;
    bt[3] := 1;
    bt[4] := 1;
    bt[5] := 1;
    bt[6] := 1;
    bt[7] := 11;
    bt[8] := 33;
    bt[9] := 22;
    disk^.writecallback(disk, 3, 1, puint32(bt));


    // puint32(buffer)[127]:= $55AA;

        console.writestringlnWND('writting to bootrecord', getTerminalHWND());
        console.writeintlnWND(start + 1, getTerminalHWND());
    console.redrawWindows;

    disk^.writecallback(disk, start + 1, 1, puint32(buffer));

        console.writestringlnWND('finished writting to bootrecord', getTerminalHWND());

    console.redrawWindows;

    fatStart:= start + 1 + bootRecord^.rsvSectors;
    dataStart:= fatStart + bootRecord^.FATSize;

    zeroBuffer:= puint32(kalloc( disk^.sectorSize ));
    memset(uint32(zeroBuffer), 0, disk^.sectorSize );

    // while true do begin
    //     if i > FATSize then break; 
    //     disk^.writecallback(disk, fatStart + i, 1, zeroBuffer);
    //     i+=1;
    // end;

    kfree(buffer);
    kfree(zeroBuffer);
    writestring('Frees');
    console.redrawWindows;
        console.writestringlnWND('writting 1', getTerminalHWND());
    console.redrawWindows;

    buffer:= puint32(kalloc(disk^.sectorSize));
    memset(uint32(buffer), 0, disk^.sectorSize);
        console.writestringlnWND('writting 1', getTerminalHWND());
    console.redrawWindows;

    puint32(buffer)[0]:= $FFFFFFF8; //fsinfo
    puint32(buffer)[1]:= $FFFFFFF8; //root cluster
    writestring('Buffer Alloc');
    console.redrawWindows;

    disk^.writecallback(disk, fatStart, 1, buffer);
    writestring('WriteCB1');
    console.redrawWindows;

    kfree(buffer);

    buffer:= puint32(kalloc(disk^.sectorsize));
    memset(uint32(buffer), 0, disk^.sectorsize);
    writestring('Buffer Alloc 2');
    console.redrawWindows;

    PDirectory(buffer)[0].fileName   := thisArray;
    PDirectory(buffer)[0].attributes := $08;
    PDirectory(buffer)[0].clusterLow := 1;

    PDirectory(buffer)[1].fileName   := parentArray;
    PDirectory(buffer)[1].attributes := $10;
    PDirectory(buffer)[1].clusterLow := 1;
    
    disk^.writecallback(disk, dataStart + (config^ * rootCluster), 1, buffer);
    writestring('WriteCB2');
    console.redrawWindows;

    kfree(buffer);
    writestring('Free');
    console.redrawWindows;

end;

procedure detect_volumes(disk : PStorage_Device);
var
    buffer : puint32;
    i : uint8;
    volume : PStorage_volume;

    dir : PDirectory;
    dirs : PLinkedListBase;
begin
    push_trace('fat32.detectVolumes()');
    console.writeintln(2);
    redrawWindows();

    volume:= PStorage_volume(kalloc(sizeof(TStorage_Volume)));
    //check first address for MBR
    //if found then add volume and use info to see if there is another volume
    buffer := puint32(kalloc(512));
    memset(uint32(buffer), 0, 512);
    disk^.readcallback(disk, 2, 1, buffer);

        console.writeintln(3);
    redrawWindows();

    if (puint32(buffer)[127] = $55AA) and (PBootRecord(buffer)^.bsignature = $29) then begin //TODO partition table
        console.writestringln('FAT32: volume found!');
        volume^.device:= disk;
        volume^.sectorStart:= 1;
        volume^.sectorSize:= PBootRecord(buffer)^.sectorSize;
        volume^.freeSectors:= 1000000; //TODO implement get free sectors need FSINFO implemented first
        volume^.filesystem := @filesystem;
        storagemanagement.register_volume(disk, volume);
    end;

    kfree(buffer);
end;

procedure init();
begin
    push_trace('fat32.init()');
    filesystem.sName:= 'FAT32'; 
    filesystem.readDirCallback:= @readDirectoryGen;
    filesystem.createDirCallback:= @writeDirectoryGen;
    filesystem.createcallback:= @create_volume;
    filesystem.detectcallback:= @detect_volumes;
    filesystem.writecallback:= @writeFile;
    filesystem.readcallback := @readFile;

    storagemanagement.register_filesystem(@filesystem);
end;

end.