MF_API bool MFFileSystem_FindNext(MFFind *pFind, MFFindData *pFindData) { GET_MODULE_DATA(MFFileSystemState); if(!(pFind->pMount->volumeInfo.flags & MFMF_DontCacheTOC)) { size_t id = (size_t)pFind->pFilesystemData + 1; for(; id < pFind->pMount->numFiles; ++id) { if(MFString_PatternMatch(pFind->searchPattern, pFind->pMount->pEntries[id].pName)) break; } if(id < pFind->pMount->numFiles) { MFString_Copy(pFindData->pFilename, pFind->pMount->pEntries[id].pName); MFString_Copy(pFindData->pSystemPath, (char*)pFind->pMount->pEntries[id].pFilesysData); pFindData->info = pFind->pMount->pEntries[id].info; pFind->pFilesystemData = (void*)id; } else return false; } else return pModuleData->ppFileSystemList[pFind->pMount->volumeInfo.fileSystem]->callbacks.FindNext(pFind, pFindData); return true; }
void MFFileSystem_UnregisterFileSystem(MFFileSystemHandle filesystemHandle) { MFDebug_Assert((uint32)filesystemHandle < gDefaults.filesys.maxFileSystems, "Invalid filesystem"); GET_MODULE_DATA(MFFileSystemState); MFFileSystem *pFS = pModuleData->ppFileSystemList[filesystemHandle]; MFDebug_Assert(pFS, "Filesystem not mounted"); if(pFS->callbacks.UnregisterFS) pFS->callbacks.UnregisterFS(); if(pFS->thread) { // terminate job thread MFJob *pJob = NewJob(pFS); PostJob(pFS, pJob); while(pJob->status != MFJS_Finished) { // yield } MFThread_DestroySemaphore(pFS->semaphore); pFS->jobs.Deinit(); MFHeap_Free(pFS->ppJobQueue); } pModuleData->gFileSystems.Destroy(pFS); pModuleData->ppFileSystemList[filesystemHandle] = NULL; }
MF_API int MFFileSystem_Dismount(const char *pMountpoint) { GET_MODULE_DATA(MFFileSystemState); MFMount *pT = MFFileSystem_FindVolume(pMountpoint); if(pT) { // dismount pModuleData->ppFileSystemList[pT->volumeInfo.fileSystem]->callbacks.FSDismount(pT); // remove mount if(pModuleData->pMountList == pT) pModuleData->pMountList = pT->pNext; if(pModuleData->pMountListEnd == pT) pModuleData->pMountListEnd = pT->pPrev; if(pT->pNext) pT->pNext->pPrev = pT->pPrev; if(pT->pPrev) pT->pPrev->pNext = pT->pNext; MFHeap_Free(pT); return 0; } // coundnt find mount.. return -1; }
MF_API void MFFileSystem_FindClose(MFFind *pFind) { GET_MODULE_DATA(MFFileSystemState); if(pFind->pMount->volumeInfo.flags & MFMF_DontCacheTOC) pModuleData->ppFileSystemList[pFind->pMount->volumeInfo.fileSystem]->callbacks.FindClose(pFind); pModuleData->gFinds.Destroy(pFind); }
MF_API int MFFile_Close(MFFile* fileHandle) { GET_MODULE_DATA(MFFileSystemState); pModuleData->ppFileSystemList[fileHandle->filesystem]->callbacks.Close(fileHandle); pModuleData->gOpenFiles.Free(fileHandle); return 0; }
MFMount* MFFileSystem_FindVolume(const char *pVolumeName) { GET_MODULE_DATA(MFFileSystemState); for(MFMount *pT = pModuleData->pMountList; pT; pT = pT->pNext) { if(!MFString_CaseCmp(pT->volumeInfo.pVolumeName, pVolumeName)) return pT; } return NULL; }
// open a file from the mounted filesystem stack MF_API MFFile* MFFileSystem_Open(const char *pFilename, uint32 openFlags) { MFDebug_Log(5, MFStr("Call: MFFileSystem_Open(\"%s\", 0x%x)", pFilename, openFlags)); GET_MODULE_DATA(MFFileSystemState); MFMount *pMount = pModuleData->pMountList; const char *pMountpoint = NULL; // search for a mountpoint size_t len = MFString_Length(pFilename); for(size_t a=0; a<len; a++) { if(pFilename[a] == ':') { pMountpoint = MFStrN(pFilename, a); pFilename += a+1; break; } if(pFilename[a] == '.') { // if we have found a dot, this cant be a mountpoint // (mountpoints may only be alphanumeric) break; } } // search for file through the mount list... while(pMount) { int onlyexclusive = pMount->volumeInfo.flags & MFMF_OnlyAllowExclusiveAccess; if((!pMountpoint && !onlyexclusive) || (pMountpoint && !MFString_CaseCmp(pMountpoint, pMount->volumeInfo.pVolumeName))) { // open the file from a mount MFFile *hFile = pModuleData->ppFileSystemList[pMount->volumeInfo.fileSystem]->callbacks.FSOpen(pMount, pFilename, openFlags); if(hFile) return hFile; } pMount = pMount->pNext; } if(!(openFlags & MFOF_TryOpen)) MFDebug_Warn(4, MFStr("MFFile_Open(\"%s\", 0x%x) - Failed to open file", pFilename, openFlags)); return NULL; }
MF_API int MFFileSystem_GetNumVolumes() { GET_MODULE_DATA(MFFileSystemState); int numVolumes = 0; MFMount *pMount = pModuleData->pMountList; while(pMount) { ++numVolumes; pMount = pMount->pNext; } return numVolumes; }
MF_API void MFFileSystem_GetVolumeInfo(int volumeID, MFVolumeInfo *pVolumeInfo) { GET_MODULE_DATA(MFFileSystemState); MFMount *pMount = pModuleData->pMountList; while(pMount && volumeID) { --volumeID; pMount = pMount->pNext; } MFDebug_Assert(pMount, "Invalid volume ID"); *pVolumeInfo = pMount->volumeInfo; }
/////////////////////////// // file access functions MF_API MFFile* MFFile_Open(MFFileSystemHandle fileSystem, MFOpenData *pOpenData) { GET_MODULE_DATA(MFFileSystemState); MFFile *pFile = (MFFile*)pModuleData->gOpenFiles.AllocAndZero(); pFile->filesystem = fileSystem; int result = pModuleData->ppFileSystemList[fileSystem]->callbacks.Open(pFile, pOpenData); if(result < 0) { pModuleData->gOpenFiles.Free(pFile); return NULL; } return pFile; }
MFInitStatus MFFileSystem_InitFileSystems(int moduleId, bool bPerformInitialisation) { GET_MODULE_DATA(MFFileSystemState); pModuleData->hNativeFileSystem = ((MFFileSystemGlobalState*)gpEngineInstance->modules[gFileSystemNativeId].pModuleData)->hFileSystemHandle; pModuleData->hMemoryFileSystem = ((MFFileSystemGlobalState*)gpEngineInstance->modules[gFileSystemMemoryId].pModuleData)->hFileSystemHandle; pModuleData->hCachedFileSystem = ((MFFileSystemGlobalState*)gpEngineInstance->modules[gFileSystemCachedFileId].pModuleData)->hFileSystemHandle; pModuleData->hZipFileSystem = ((MFFileSystemGlobalState*)gpEngineInstance->modules[gFileSystemZipFileId].pModuleData)->hFileSystemHandle; pModuleData->hHTTPFileSystem = ((MFFileSystemGlobalState*)gpEngineInstance->modules[gFileSystemHTTPId].pModuleData)->hFileSystemHandle; // call the filesystem init callback MFSystemCallbackFunction pFilesystemInitCallback = MFSystem_GetSystemCallback(MFCB_FileSystemInit); if(pFilesystemInitCallback) pFilesystemInitCallback(); return MFIS_Succeeded; }
MFFileSystemHandle MFFileSystem_RegisterFileSystem(const char *pFilesystemName, MFFileSystemCallbacks *pCallbacks) { MFDebug_Log(5, MFStr("Call: MFFileSystem_RegisterFileSystem(\"%s\")", pFilesystemName)); GET_MODULE_DATA(MFFileSystemState); for(uint32 a=0; a<gDefaults.filesys.maxFileSystems; a++) { if(pModuleData->ppFileSystemList[a] == NULL) { MFDebug_Assert(pCallbacks->Open, "No Open function supplied."); MFDebug_Assert(pCallbacks->Close, "No Close function supplied."); MFDebug_Assert(pCallbacks->Read, "No Read function supplied."); MFDebug_Assert(pCallbacks->Write, "No Write function supplied."); MFDebug_Assert(pCallbacks->Seek, "No Seek function supplied."); MFFileSystem *pFS = pModuleData->gFileSystems.Create(); MFZeroMemory(pFS, sizeof(MFFileSystem)); MFString_Copy(pFS->name, pFilesystemName); MFCopyMemory(&pFS->callbacks, pCallbacks, sizeof(MFFileSystemCallbacks)); pModuleData->ppFileSystemList[a] = pFS; #if defined(USE_JOB_THREAD) pFS->ppJobQueue = (MFJob**)MFHeap_Alloc(sizeof(MFJob*)*MAX_JOBS); pFS->jobs.Init(MFStr("%s Job List", pFilesystemName), MAX_JOBS+2); pFS->readJob = 0; pFS->writeJob = 0; pFS->numJobs = MAX_JOBS; pFS->semaphore = MFThread_CreateSemaphore("Filesystem Semaphore", MAX_JOBS, 0); pFS->thread = MFThread_CreateThread(MFStr("%s Thread", pFilesystemName), MKFileJobThread, pFS, MFPriority_AboveNormal); #endif if(pFS->callbacks.RegisterFS) pFS->callbacks.RegisterFS(); return a; } } MFDebug_Assert(false, MFStr("Exceeded maximum of %d Filesystems. Modify 'gDefaults.filesys.maxFileSystems'.", gDefaults.filesys.maxFileSystems)); return -1; }
// return a handle to a specific filesystem MF_API MFFileSystemHandle MFFileSystem_GetInternalFileSystemHandle(MFFileSystemHandles fileSystemHandle) { GET_MODULE_DATA(MFFileSystemState); switch(fileSystemHandle) { case MFFSH_NativeFileSystem: MFDebug_Assert(pModuleData->hNativeFileSystem > -1, "Native filesystem is not available..."); if(pModuleData->hNativeFileSystem < 0) MFDebug_Error("Native filesystem is not available..."); return pModuleData->hNativeFileSystem; case MFFSH_MemoryFileSystem: MFDebug_Assert(pModuleData->hMemoryFileSystem > -1, "Memory file filesystem is not available..."); if(pModuleData->hMemoryFileSystem < 0) MFDebug_Error("Memory file filesystem is not available..."); return pModuleData->hMemoryFileSystem; case MFFSH_CachedFileSystem: MFDebug_Assert(pModuleData->hCachedFileSystem > -1, "Memory file filesystem is not available..."); if(pModuleData->hCachedFileSystem < 0) MFDebug_Error("Cached file filesystem is not available..."); return pModuleData->hCachedFileSystem; case MFFSH_ZipFileSystem: MFDebug_Assert(pModuleData->hZipFileSystem > -1, "Zip file filesystem is not available..."); if(pModuleData->hZipFileSystem < 0) MFDebug_Error("Zip file filesystem is not available..."); return pModuleData->hZipFileSystem; case MFFSH_HTTPFileSystem: MFDebug_Assert(pModuleData->hHTTPFileSystem > -1, "HTTP file filesystem is not available..."); if(pModuleData->hHTTPFileSystem < 0) MFDebug_Error("HTTP file filesystem is not available..."); return pModuleData->hHTTPFileSystem; case MFFSH_FTPFileSystem: MFDebug_Assert(pModuleData->hFTPFileSystem > -1, "FTP file filesystem is not available..."); if(pModuleData->hFTPFileSystem < 0) MFDebug_Error("FTP file filesystem is not available..."); return pModuleData->hFTPFileSystem; default: MFDebug_Error(MFStr("Invalid filesystem handle: %d", fileSystemHandle)); return -1; } }
void MFFileSystem_DeinitModule() { GET_MODULE_DATA(MFFileSystemState); if(pModuleData->hDataArchive) { MFFile_Close(pModuleData->hDataArchive); } MFFileSystemHTTP_DeinitModule(); MFFileSystemZipFile_DeinitModule(); MFFileSystemCachedFile_DeinitModule(); MFFileSystemMemory_DeinitModule(); MFFileSystemNative_DeinitModule(); MFHeap_Free(pModuleData->ppFileSystemList); pModuleData->gFileSystems.Deinit(); pModuleData->gOpenFiles.Deinit(); pModuleData->gFinds.Deinit(); }
MF_API const char *MFFileSystem_ResolveSystemPath(const char *pFilename, bool bAbsolute) { MFFile *pFile = MFFileSystem_Open(pFilename, MFOF_Read|MFOF_Binary); const char *pPath = NULL; if(pFile) { GET_MODULE_DATA(MFFileSystemState); // TODO: support other filesystems that forward to the native filesystem (like cache filesystem?) if(pFile->filesystem == pModuleData->hNativeFileSystem) pPath = MFStr(pFile->fileIdentifier); MFFile_Close(pFile); } // convert to absolute... if(bAbsolute) pPath = MFFileNative_MakeAbsolute(pPath); return pPath; }
// mount a filesystem MF_API int MFFileSystem_Mount(MFFileSystemHandle fileSystem, MFMountData *pMountData) { GET_MODULE_DATA(MFFileSystemState); MFMount *pMount; pMount = (MFMount*)MFHeap_AllocAndZero(sizeof(MFMount) + MFString_Length(pMountData->pMountpoint) + 1); pMount->volumeInfo.flags = pMountData->flags; pMount->volumeInfo.fileSystem = fileSystem; pMount->volumeInfo.priority = pMountData->priority; pMount->volumeInfo.pVolumeName = (const char*)&pMount[1]; MFString_Copy((char*)&pMount[1], pMountData->pMountpoint); // call the mount callback int result = pModuleData->ppFileSystemList[fileSystem]->callbacks.FSMount(pMount, pMountData); if(result < 0) { MFHeap_Free(pMount); return -1; } return MFFileSystem_AddVolume(pMount); }
int MFFileSystem_AddVolume(MFMount *pMount) { GET_MODULE_DATA(MFFileSystemState); // hook it up.. if(!pModuleData->pMountList) { pModuleData->pMountList = pModuleData->pMountListEnd = pMount; pMount->pPrev = pMount->pNext = NULL; } else { MFMount *pT = pModuleData->pMountList; while(pT && pT->volumeInfo.priority < pMount->volumeInfo.priority) pT = pT->pNext; if(pT) { if(pT == pModuleData->pMountList) pModuleData->pMountList = pMount; pMount->pPrev = pT->pPrev; pMount->pNext = pT; pT->pPrev = pMount; if(pMount->pPrev) pMount->pPrev->pNext = pMount; } else { pMount->pPrev = pModuleData->pMountListEnd; pModuleData->pMountListEnd->pNext = pMount; pModuleData->pMountListEnd = pMount; } } // build toc if(!(pMount->volumeInfo.flags & MFMF_DontCacheTOC)) { MFDebug_Assert(pModuleData->ppFileSystemList[pMount->volumeInfo.fileSystem]->callbacks.FindFirst, "Filesystem must provide a set of find functions to cache the TOC"); MFFindData findData; MFFind *hFind; bool flatten = (pMount->volumeInfo.flags & MFMF_FlattenDirectoryStructure) != 0; bool recursive = (pMount->volumeInfo.flags & MFMF_Recursive) != 0; pMount->volumeInfo.flags |= MFMF_DontCacheTOC; const char *pFindPath = MFStr("%s:", pMount->volumeInfo.pVolumeName); // this is a crude way to check if the directory exists.. // TODO: improve this!! hFind = MFFileSystem_FindFirst(MFStr("%s*", pFindPath), &findData); if(!hFind) { MFDebug_Warn(1, "FileSystem: Couldnt Mount FileSystem."); return -1; } MFFileSystem_FindClose(hFind); // build the TOC size_t stringCacheSize = 0; pMount->numFiles = MFFileSystem_GetNumEntries(pFindPath, recursive, flatten, &stringCacheSize); int sizeOfToc = sizeof(MFTOCEntry)*pMount->numFiles; MFTOCEntry *pTOC = (MFTOCEntry*)MFHeap_Alloc(sizeOfToc + stringCacheSize); char *pStringCache = (char*)pTOC + sizeOfToc; MFFileSystem_BuildToc(pFindPath, pTOC, NULL, pStringCache, recursive, flatten); pMount->pEntries = pTOC; pMount->volumeInfo.flags &= ~MFMF_DontCacheTOC; } return 0; }
MF_API bool MFFile_Delete(MFFileSystemHandle fileSystem, const char *pPath, bool bRecursive) { GET_MODULE_DATA(MFFileSystemState); return pModuleData->ppFileSystemList[fileSystem]->callbacks.Delete(pPath, bRecursive); }
MF_API bool MFFile_CreateDirectory(MFFileSystemHandle fileSystem, const char *pPath) { GET_MODULE_DATA(MFFileSystemState); return pModuleData->ppFileSystemList[fileSystem]->callbacks.CreateDir(pPath); }
// offset management (these are stdio function signature compliant) MF_API uint64 MFFile_Seek(MFFile* fileHandle, int64 bytes, MFFileSeek relativity) { GET_MODULE_DATA(MFFileSystemState); return pModuleData->ppFileSystemList[fileHandle->filesystem]->callbacks.Seek(fileHandle, bytes, relativity); }
MF_API size_t MFFile_Write(MFFile* fileHandle, const void *pBuffer, size_t bytes, bool async) { GET_MODULE_DATA(MFFileSystemState); return pModuleData->ppFileSystemList[fileHandle->filesystem]->callbacks.Write(fileHandle, pBuffer, bytes); }
MF_API MFFind* MFFileSystem_FindFirst(const char *pSearchPattern, MFFindData *pFindData) { GET_MODULE_DATA(MFFileSystemState); const char *pMountpoint = NULL; MFFind *pFind = NULL; // search for a mountpoint const char *pColon = MFString_Chr(pSearchPattern, ':'); if(pColon) { pMountpoint = MFStrN(pSearchPattern, pColon - pSearchPattern); pSearchPattern = pColon + 1; } MFDebug_Assert(pMountpoint, "A volume name must be specified in the search pattern."); // find the volume MFMount *pMount = MFFileSystem_FindVolume(pMountpoint); if(!pMount) { MFDebug_Warn(2, MFStr("MFFileSystem_FindFirst: Volume '%s' in not mounted.", pMountpoint)); return NULL; } // search for file through the mount list... if(!(pMount->volumeInfo.flags & MFMF_DontCacheTOC)) { if(pMount->numFiles) { pFind = pModuleData->gFinds.Create(); pFind->pMount = pMount; MFString_Copy(pFind->searchPattern, pSearchPattern); size_t file = 0; for(; file < pFind->pMount->numFiles; ++file) { if(MFString_PatternMatch(pSearchPattern, pMount->pEntries[file].pName)) break; } if(file == pFind->pMount->numFiles) { pModuleData->gFinds.Destroy(pFind); pFind = NULL; } else { pFind->pFilesystemData = (void*)file; MFString_Copy(pFindData->pFilename, pMount->pEntries[file].pName); MFString_Copy(pFindData->pSystemPath, (char*)pMount->pEntries[file].pFilesysData); pFindData->info = pMount->pEntries[file].info; } } } else { pFind = pModuleData->gFinds.Create(); pFind->pMount = pMount; pFind->pFilesystemData = NULL; MFString_Copy(pFind->searchPattern, pSearchPattern); if(!pModuleData->ppFileSystemList[pMount->volumeInfo.fileSystem]->callbacks.FindFirst(pFind, pSearchPattern, pFindData)) { pModuleData->gFinds.Destroy(pFind); pFind = NULL; } } return pFind; }
MF_API bool MFFile_Stat(MFFileSystemHandle fileSystem, const char *pPath, MFFileInfo *pFileInfo) { GET_MODULE_DATA(MFFileSystemState); return pModuleData->ppFileSystemList[fileSystem]->callbacks.Stat(pPath, pFileInfo); }
void MFFileSystem_RegisterDefaultArchives(void *) { GET_MODULE_DATA(MFFileSystemState); MFFile *&hDataArchive = pModuleData->hDataArchive; MFFile *&hPatchArchive = pModuleData->hPatchArchive; // try and mount the 'standard' archives... // TODO: ponder removing this code and forcing the use of a filesystem init callback? :/ MFOpenDataNative dataArchive; dataArchive.cbSize = sizeof(MFOpenDataNative); dataArchive.openFlags = MFOF_Read|MFOF_Binary; hDataArchive = 0; const char **pArc = gArchiveNames; while(!hDataArchive && *pArc) { dataArchive.pFilename = MFFile_SystemPath(MFStr(*pArc, MFSystem_GetPlatformString(MFSystem_GetCurrentPlatform()))); hDataArchive = MFFile_Open(pModuleData->hNativeFileSystem, &dataArchive); ++pArc; } MFMountDataNative mountData; mountData.cbSize = sizeof(MFMountDataNative); mountData.priority = MFMP_Normal; if(hDataArchive) { // attempt to cache the zip archive MFOpenDataCachedFile cachedOpen; cachedOpen.cbSize = sizeof(MFOpenDataCachedFile); cachedOpen.openFlags = MFOF_Read | MFOF_Binary | MFOF_Cached_CleanupBaseFile; cachedOpen.maxCacheSize = 1*1024*1024; // 1mb cache for zip archives should be heaps!! cachedOpen.pBaseFile = hDataArchive; MFFile *pCachedFile = MFFile_Open(MFFileSystem_GetInternalFileSystemHandle(MFFSH_CachedFileSystem), &cachedOpen); if(pCachedFile) hDataArchive = pCachedFile; // mount the zip archive. MFMountDataZipFile zipMountData; zipMountData.cbSize = sizeof(MFMountDataZipFile); zipMountData.flags = MFMF_Recursive|MFMF_FlattenDirectoryStructure; zipMountData.priority = MFMP_Normal; zipMountData.pMountpoint = "data"; zipMountData.pZipArchive = hDataArchive; MFFileSystem_Mount(pModuleData->hZipFileSystem, &zipMountData); } mountData.flags = MFMF_DontCacheTOC; mountData.pMountpoint = "home"; mountData.pPath = MFFile_HomePath(); MFFileSystem_Mount(pModuleData->hNativeFileSystem, &mountData); // see if we can mount the patch archive.. hPatchArchive = 0; pArc = gPatchArchiveNames; while(!hPatchArchive && *pArc) { dataArchive.pFilename = MFFile_SystemPath(MFStr(*pArc, MFSystem_GetPlatformString(MFSystem_GetCurrentPlatform()))); hDataArchive = MFFile_Open(pModuleData->hNativeFileSystem, &dataArchive); ++pArc; } if(hPatchArchive) { // attempt to cache the zip archive MFOpenDataCachedFile cachedOpen; cachedOpen.cbSize = sizeof(MFOpenDataCachedFile); cachedOpen.openFlags = MFOF_Read | MFOF_Binary | MFOF_Cached_CleanupBaseFile; cachedOpen.maxCacheSize = 1*1024*1024; // 1mb cache for zip archives should be heaps!! cachedOpen.pBaseFile = hPatchArchive; MFFile *pCachedFile = MFFile_Open(MFFileSystem_GetInternalFileSystemHandle(MFFSH_CachedFileSystem), &cachedOpen); if(pCachedFile) hPatchArchive = pCachedFile; // mount the zip archive. MFMountDataZipFile zipMountData; zipMountData.cbSize = sizeof(MFMountDataZipFile); zipMountData.flags = MFMF_Recursive|MFMF_FlattenDirectoryStructure; zipMountData.priority = MFMP_VeryHigh; zipMountData.pMountpoint = "patch"; zipMountData.pZipArchive = hPatchArchive; MFFileSystem_Mount(pModuleData->hZipFileSystem, &zipMountData); } if(pModuleData->hHTTPFileSystem != -1) { // register the network filesystems MFMountDataHTTP mountDataHTTP; mountDataHTTP.cbSize = sizeof(MFMountDataHTTP); mountDataHTTP.pMountpoint = "http"; mountDataHTTP.priority = MFMP_Normal + 1; mountDataHTTP.flags = MFMF_OnlyAllowExclusiveAccess; MFFileSystem_Mount(pModuleData->hHTTPFileSystem, &mountDataHTTP); } }