#include <kernel/driver.h>
#include <os/stream.h>
#include "fat.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#define FAT_BYTES 2 // FAT16
// #define FAT_BYTES 4 // FAT32 (nyi)
#define CLUSTER_CACHE 1
#if 0
#define TRACE wprintf
#else
int TRACE(...)
{
return 0;
}
#endif
extern "C" bool wildcard(const wchar_t* str, const wchar_t* spec);
struct folderitem_fat_t : folderitem_t
{
folderitem_fat_t* next;
IUnknown* mount;
};
class FatFile : public IUnknown, public IStream
{
protected:
fat_filefind_t m_stat;
addr_t m_dwPosition;
byte* m_pCache;
dword m_dwCacheSize, m_dwCachePtr;
IBlockDevice* m_pDevice;
dword m_dwReadCluster;
fat_bootsector_t m_bpb;
bool m_bIsRoot;
dword GetNextCluster(dword dwCluster);
bool ReadCluster(dword dwCluster, void* pBuf, size_t size);
friend class FatFolder;
public:
FatFile(IBlockDevice* pDev, const fat_filefind_t* stat);
virtual ~FatFile();
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject);
IMPLEMENT_IUNKNOWN;
STDMETHOD_(size_t, Read) (void* pBuffer, size_t dwLength);
STDMETHOD_(size_t, Write) (const void* pBuffer, size_t dwLength);
STDMETHOD(SetIoMode)(dword mode);
STDMETHOD(IsReady)();
STDMETHOD(Stat)(folderitem_t* buf);
};
class FatFolder : public IUnknown, public IFolder
{
public:
FatFolder(IBlockDevice* pDev, const fat_filefind_t* stat, bool bIsRoot);
virtual ~FatFolder();
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject);
IMPLEMENT_IUNKNOWN;
STDMETHOD(FindFirst)(const wchar_t* spec, folderitem_t* buf);
STDMETHOD(FindNext)(folderitem_t* buf);
STDMETHOD(FindClose)(folderitem_t* pBuf);
STDMETHOD(Open)(folderitem_t* item, const wchar_t* params);
STDMETHOD(Mount)(const wchar_t* name, IUnknown* obj);
protected:
FatFile m_file;
folderitem_fat_t* m_item_first;
void ScanDir();
};
extern "C" IFolder* FatRoot_Create(IBlockDevice* pDevice)
{
fat_filefind_t buf =
{
{
sizeof(buf),
NULL,
NULL,
NULL,
0,
0,
0
},
{
"",
"",
0,
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
0, 0, 0, 0
}
};
return new FatFolder(pDevice, &buf, true);
}
/****************************************************************************
* FatFile *
****************************************************************************/
FatFile::FatFile(IBlockDevice* pDev, const fat_filefind_t* stat)
{
m_pDevice = pDev;
IUnknown_AddRef(pDev);
memset(&m_bpb, 0, sizeof(m_bpb));
//pDev->Seek(0, SEEK_SET);
//pDev->ReadFile(&m_bpb, sizeof(m_bpb));
IBlockDevice_BlockRead(pDev, 0, 1, &m_bpb);
m_stat = *stat;
m_dwReadCluster = (dword) -1;
m_bIsRoot = false;
m_dwCacheSize = m_bpb.nBytesPerSector * m_bpb.nSectorsPerCluster *
CLUSTER_CACHE;
m_dwCachePtr = (dword) -1;
m_pCache = (byte*) malloc(m_dwCacheSize);
}
FatFile::~FatFile()
{
TRACE(L"[fatfile] deleting\n");
if (m_pCache)
free(m_pCache);
if (m_pDevice)
IUnknown_Release(m_pDevice);
}
HRESULT FatFile::QueryInterface(REFIID iid, void ** ppvObject)
{
if (InlineIsEqualGUID(iid, IID_IUnknown) ||
InlineIsEqualGUID(iid, IID_IStream))
{
AddRef();
*ppvObject = (IStream*) this;
return S_OK;
}
else
return E_FAIL;
}
size_t FatFile::Read(void* pBuffer, size_t dwLength)
{
size_t dwRead = 0;
int i;
if (m_dwReadCluster == (dword) -1)
{
if (m_dwPosition == 0)
m_dwReadCluster = m_stat.dirent.nFirstCluster;
else
return 0;
}
while (dwRead < dwLength)
{
if (m_dwCachePtr >= m_dwCacheSize)
{
TRACE(L"[clus = %x] ", m_dwReadCluster);
if (m_dwReadCluster >= 0xFFF8)
{
m_dwPosition += dwRead;
m_dwCachePtr = (dword) -1;
return dwRead;
}
if (!ReadCluster(m_dwReadCluster, m_pCache, m_dwCacheSize))
{
TRACE(L"ReadCluster failed\n");
m_dwPosition += dwRead;
m_dwCachePtr = (dword) -1;
return dwRead;
}
m_dwCachePtr = 0;
m_dwReadCluster = GetNextCluster(m_dwReadCluster);
}
TRACE(L"{cache = %d} ", m_dwCacheSize - m_dwCachePtr);
i = min(m_dwCacheSize - m_dwCachePtr, dwLength - dwRead);
memcpy((byte*) pBuffer + dwRead, m_pCache + m_dwCachePtr, i);
m_dwCachePtr += i;
dwRead += i;
}
m_dwPosition += dwLength;
return dwRead;
}
size_t FatFile::Write(const void* pBuffer, size_t dwLength)
{
return 0;
}
HRESULT FatFile::SetIoMode(dword mode)
{
return S_OK;
}
HRESULT FatFile::IsReady()
{
return S_OK;
}
HRESULT FatFile::Stat(folderitem_t* buf)
{
if (buf->size == sizeof(fat_filefind_t))
memcpy(buf, &m_stat, sizeof(fat_filefind_t));
else
*buf = m_stat.ff;
return S_OK;
}
dword FatFile::GetNextCluster(dword dwCluster)
{
dword FATOffset, ThisFATSecNum, ThisFATEntOffset;
byte sector[512];
FATOffset = dwCluster * FAT_BYTES;
ThisFATSecNum = m_bpb.nReservedSectors +
(FATOffset / m_bpb.nBytesPerSector);
ThisFATEntOffset = FATOffset % m_bpb.nBytesPerSector;
//m_pDevice->Seek(ThisFATSecNum, SEEK_SET);
//m_pDevice->ReadFile(sector, sizeof(sector));
IBlockDevice_BlockRead(m_pDevice, ThisFATSecNum, 1, sector);
return *(word*) (sector + ThisFATEntOffset);
}
bool FatFile::ReadCluster(dword dwCluster, void* pBuf, size_t size)
{
dword RootDirSectors, FirstDataSector, FirstSectorofCluster;
if (m_bIsRoot)
RootDirSectors = 0;
else
RootDirSectors =
((m_bpb.nRootDirectoryEntries * 32) + (m_bpb.nBytesPerSector - 1))
/ m_bpb.nBytesPerSector;
FirstDataSector = m_bpb.nReservedSectors +
(m_bpb.nFatCount * m_bpb.nSectorsPerFat) + RootDirSectors;
if (m_bIsRoot)
dwCluster += 2;
FirstSectorofCluster = ((dwCluster - 2) * m_bpb.nSectorsPerCluster) +
FirstDataSector;
//m_pDevice->Seek(FirstSectorofCluster, SEEK_SET);
//return m_pDevice->ReadFile(pBuf, size) == (int) size;
size /= 512;
return IBlockDevice_BlockRead(m_pDevice, FirstSectorofCluster, size, pBuf) == size;
}
/****************************************************************************
* FatFolder *
****************************************************************************/
FatFolder::FatFolder(IBlockDevice* pDev, const fat_filefind_t* stat, bool bIsRoot) :
m_file(pDev, stat)
{
m_file.m_bIsRoot = bIsRoot;
m_item_first = NULL;
}
FatFolder::~FatFolder()
{
folderitem_fat_t *item, *next;
for (item = m_item_first; item; item = next)
{
next = item->next;
if (item->mount)
IUnknown_Release(item->mount);
item->mount = NULL;
delete item;
}
}
HRESULT FatFolder::QueryInterface(REFIID iid, void ** ppvObject)
{
if (InlineIsEqualGUID(iid, IID_IUnknown) ||
InlineIsEqualGUID(iid, IID_IFolder))
{
AddRef();
*ppvObject = (IFolder*) this;
return S_OK;
}
else if (InlineIsEqualGUID(iid, IID_IStream))
{
AddRef();
*ppvObject = (IStream*) &m_file;
return S_OK;
}
else
return E_FAIL;
}
HRESULT FatFolder::FindFirst(const wchar_t* szSpec, folderitem_t* item)
{
item->spec = wcsdup(szSpec);
//TRACE(L"FindFirst %s\n", item->spec);
return S_OK;
}
HRESULT FatFolder::FindNext(folderitem_t* item)
{
union
{
fat_dirent_t dirent;
fat_lfnslot_t lfn;
};
int i, j;
wchar_t name[MAX_PATH], temp[14], *nameptr;
while (1)
{
if (m_file.Read(&dirent, sizeof(dirent)) < sizeof(dirent))
{
TRACE(L"End of file");
return E_FAIL;
}
if (dirent.szFilename[0] == 0)
{
TRACE(L"End of directory\n");
return E_FAIL;
}
if (dirent.szFilename[0] != 0xe5)
{
memset(name, 0, sizeof(name));
if ((dirent.nAttributes & ATTR_LONG_NAME) != ATTR_LONG_NAME)
{
for (i = 0; i < 8; i++)
{
if (dirent.szFilename[i] == ' ')
{
name[i] = 0;
break;
}
else if (iswupper(dirent.szFilename[i]))
name[i] = towlower(dirent.szFilename[i]);
else
name[i] = dirent.szFilename[i];
}
if (dirent.szExtension[0] != ' ')
{
wcscat(name, L".");
j = wcslen(name);
for (i = 0; i < 3; i++)
{
if (dirent.szExtension[i] == ' ')
{
name[i + j] = 0;
break;
}
else if (iswupper(dirent.szExtension[i]))
name[i + j] = towlower(dirent.szExtension[i]);
else
name[i + j] = dirent.szExtension[i];
}
}
TRACE(L"short: name = \"%s\" (\"%s\")\n", name, item->spec);
}
else
{
while ((dirent.nAttributes & ATTR_LONG_NAME)
== ATTR_LONG_NAME)
{
TRACE(L"<lfn seq=%2x> ", dirent.szFilename[0]);
if (dirent.szFilename[0] != 0xe5)
{
memset(temp, 0, sizeof(temp));
nameptr = temp;
for (i = 0; i < 5; i++)
{
*nameptr = lfn.name0_4[i];
nameptr++;
}
for (i = 0; i < 6; i++)
{
*nameptr = lfn.name5_10[i];
nameptr++;
}
for (i = 0; i < 2; i++)
{
*nameptr = lfn.name11_12[i];
nameptr++;
}
i = wcslen(temp);
memmove(name + i, name, wcslen(name) * sizeof(wchar_t));
memcpy(name, temp, i * sizeof(wchar_t));
}
if (m_file.Read(&dirent, sizeof(dirent)) < sizeof(dirent))
{
TRACE(L"End of file");
return E_FAIL;
}
}
//TRACE(L"long: name = \"%s\" (\"%s\")\n", name, item->spec);
}
if (wildcard(name, item->spec))
{
//TRACE(L"Found item size = %d\n", dirent.dwFileSize);
wcscpy(item->name, name);
item->length = dirent.dwFileSize;
item->attributes = dirent.nAttributes;
if (item->size == sizeof(fat_filefind_t))
memcpy((fat_filefind_t*) (item + 1), &dirent, sizeof(dirent));
return S_OK;
}
}
}
return E_FAIL;
}
HRESULT FatFolder::FindClose(folderitem_t* pBuf)
{
//TRACE(L"FindClose\n");
free((void*) pBuf->spec);
m_file.m_dwPosition = 0;
m_file.m_dwCachePtr = (dword) -1;
m_file.m_dwReadCluster = (dword) -1;
return S_OK;
}
HRESULT FatFolder::Open(folderitem_t* item, const wchar_t* params)
{
fat_filefind_t buf;
IUnknown* pFile;
wchar_t temp[MAX_PATH];
TRACE(L"[OpenChild] %s\n", item->name);
buf.ff.size = sizeof(fat_filefind_t);
buf.ff.name = temp;
buf.ff.name_max = countof(temp);
if (FAILED(FindFirst(item->name, &buf.ff)) ||
FAILED(FindNext(&buf.ff)))
return E_FAIL;
if (buf.ff.attributes & ATTR_DIRECTORY)
{
TRACE(L"Found dir at %x\n", buf.dirent.nFirstCluster);
pFile = new FatFolder(m_file.m_pDevice, &buf, false);
}
else
{
TRACE(L"Found file at %x\n", buf.dirent.nFirstCluster);
pFile = new FatFile(m_file.m_pDevice, &buf);
}
//FindClose((FileFind*) &buf);
if (item->size == sizeof(fat_filefind_t))
memcpy(item, &buf, sizeof(fat_filefind_t));
else
*item = buf.ff;
item->u.item_handle = pFile;
FindClose(&buf.ff);
//return pFile;
return S_OK;
}
HRESULT FatFolder::Mount(const wchar_t* name, IUnknown* obj)
{
folderitem_fat_t* item = new folderitem_fat_t;
item->size = sizeof(folderitem_fat_t);
memset(item, 0, sizeof(item));
item->name = wcsdup(name);
item->attributes = ATTR_DIRECTORY | ATTR_LINK;
item->length = 0;
if (m_item_first)
m_item_first->next = item;
else
m_item_first = item;
return S_OK;
}
void FatFolder::ScanDir()
{
}