// See RedSea File System U0 RedSeaFreeFreeList(CDrive *drive) { CFreeList *tmpf, *tmpf1; Bool unlock; try { unlock = DriveLock(drive); if (tmpf = drive->next_free) { while (tmpf != &drive->next_free) { tmpf1 = tmpf->next; Free(tmpf); tmpf = tmpf1; } } drive->next_free = NULL; if (unlock) DriveUnlock(drive); } catch if (unlock) DriveUnlock(drive); } U0 RedSeaFreeListBuild(CDrive *drive) { Bool unlock; CFreeList *tmpf; I64 i, first = drive->data_area, max_blk = drive->size + drive->drv_offset; try { unlock = DriveLock(drive); if (drive->next_free) RedSeaFreeFreeList(drive); QueueInit(&drive->next_free); while (first < max_blk) { i = 0; //count free clus while (first + i < max_blk) { DriveFATBlkSet(drive, first + i); if (Bt(drive->cur_fat_blk, (first + i - drive->data_area) & (BLK_SIZE << 3 - 1))) break; else i++; } if (i) { tmpf = ZMAlloc(sizeof(CFreeList)); tmpf->size = i; tmpf->start = first; QueueInsert(tmpf, drive->last_free); } first += i + 1; } if (unlock) DriveUnlock(drive); } catch if (unlock) DriveUnlock(drive); } U0 RedSeaInit(CDrive *drive) { CRedSeaBoot br; Bool unlock; try { unlock = DriveLock(drive); BlkRead(drive, &br, drive->drv_offset, 1); if (br.signature != MBR_PT_REDSEA || br.signature2 != 0xAA55) throw('Drive'); drive->fs_type = FSt_REDSEA; RedSeaFreeFreeList(drive); drive->spc = 1; drive->size = br.sects; drive->data_area = drive->drv_offset + br.bitmap_sects; drive->root_clus = br.root_clus; drive->fat1 = drive->fat2 = drive->drv_offset + 1; DriveFATBlkAlloc(drive); if (unlock) DriveUnlock(drive); } catch if (unlock) DriveUnlock(drive); } Bool RedSeaValidate(U8 drv_let) { CDrive *drive; CRedSeaBoot br; if ((drive = Letter2Drive(drv_let, FALSE)) && drive->fs_type == FSt_REDSEA && BlkRead(drive, &br, drive->drv_offset, 1) && br.signature == MBR_PT_REDSEA && br.signature2 == 0xAA55) return TRUE; else return FALSE; } U0 RedSeaFormat(U8 drv_let, Bool quick=TRUE) { U8 *root_dir; CDirEntry *d_native; CRedSeaBoot *br = CAlloc(BLK_SIZE); CDrive *drive = Letter2Drive(drv_let); I64 i, n, root_dir_blks; try { DriveLock(drive); // DriveTypeSet(drv_let, FSt_REDSEA); DriveTypeSet(drv_let, FSt_FAT32); drive->fs_type = FSt_REDSEA; br->signature = MBR_PT_REDSEA; br->signature2 = 0xAA55; br->drv_offset = drive->drv_offset; //For CD/DVD image copy. br->sects = drive->size; n = (br->sects + BLK_SIZE << 3 - 1) / BLK_SIZE << 3; br->bitmap_sects = n; br->unique_id = TSCGet^Now()(U64); br->root_clus = 0; if (quick) i = n + 1; else i = drive->size; BlkWriteZero(drive, drive->drv_offset, i); BlkWrite(drive, br, drive->drv_offset, 1); RedSeaInit(drive); ClusAlloc(drive, 0, 1, FALSE); //Alloc #1 root_dir_blks = MaxI64(1, drive->bd->init_root_dir_blks); br->root_clus = ClusAlloc(drive, 0, root_dir_blks, FALSE); BlkWrite(drive, br, drive->drv_offset, 1); root_dir = CAlloc(BLK_SIZE * root_dir_blks); d_native = root_dir - offset(CDirEntry.start); d_native->attr = RS_ATTR_DIR | RS_ATTR_CONTIGUOUS; *d_native->name = '.'; d_native->clus = br->root_clus; d_native->size = BLK_SIZE * root_dir_blks; d_native->datetime = Now; d_native(U8 *) += CDIR_SIZE; *d_native->name = '.'; d_native->name[1] = '.'; d_native->attr = RS_ATTR_DIR | RS_ATTR_CONTIGUOUS; d_native->clus = br->root_clus; d_native->datetime = Now; BlkWrite(drive, root_dir, br->root_clus, root_dir_blks); RedSeaInit(drive); DriveUnlock(drive); } catch DriveUnlock(drive); Free(br); Free(root_dir); } Bool RedSeaFileFind(CDrive *drive, I64 cur_dir_clus, U8 *name, CDirEntry *_res, I64 fuf_flags=0) {//FUF_JUST_DIRS, FUF_JUST_FILES CDirEntry *buf, *buf2, *ptr; U8 dname[CDIR_FILENAME_LEN]; I64 ch; Bool res = FALSE, unlock; if (fuf_flags & ~FUG_FILE_FIND) throw('FUF'); MemSet(_res, 0, sizeof(CDirEntry)); DriveCheck(drive); if (drive->fs_type != FSt_REDSEA) PrintErr("Not RedSea Drive\n"); else if (!CFileNameTo(dname, name)) PrintErr("Invalid FileName: \"%s\".\n", name); else try { unlock = DriveLock(drive); buf2 = MAlloc(BLK_SIZE); BlkRead(drive, buf2, cur_dir_clus, 1); ptr = buf2(U8 *) - offset(CDirEntry.start); buf = MAlloc(ptr->size); BlkRead(drive, buf, cur_dir_clus, ptr->size >> BLK_SIZE_BITS); Free(buf2); ptr = buf(U8 *) - offset(CDirEntry.start); *ptr->name = '.'; ptr->name[1] = 0; while (TRUE) { if (!(ch = *ptr->name)) break; else if (!(ptr->attr & RS_ATTR_DELETED) && !(fuf_flags & FUF_JUST_DIRS && !(ptr->attr & RS_ATTR_DIR)) && !(fuf_flags & FUF_JUST_FILES && ptr->attr & RS_ATTR_DIR) && !StrCompare(dname, ptr->name)) { MemCopy(&_res->attr, &ptr->attr, CDIR_SIZE); res = TRUE; goto rsff_done; } ptr(U8 *) += CDIR_SIZE; } rsff_done: Free(buf); if (unlock) DriveUnlock(drive); } catch if (unlock) DriveUnlock(drive); return res; } U8 *RedSeaFileRead(CDrive *drive, U8 *cur_dir, U8 *filename, I64 *_size, I64 *_attr) { U8 *buf = NULL; CDirEntry de; I64 c, blk_count, cur_dir_clus; DriveCheck(drive); *_size = 0; *_attr = 0; if (drive->fs_type != FSt_REDSEA) PrintErr("Not RedSea Drive\n"); else try { DriveLock(drive); cur_dir_clus = Name2DirClus(drive, cur_dir); if (RedSeaFileFind(drive, cur_dir_clus, filename, &de, FUF_JUST_FILES)) { blk_count = (de.size + BLK_SIZE - 1) >> BLK_SIZE_BITS; buf = MAlloc(blk_count << BLK_SIZE_BITS + 1); c = de.clus; c = BlkRead(drive, buf, c, blk_count); buf[de.size] = 0; //Terminate *_size = de.size; *_attr = FileAttr(de.name, de.attr); } DriveUnlock(drive); } catch DriveUnlock(drive); return buf; } Bool RedSeaCd(U8 *name, I64 cur_dir_clus) { CDirEntry de; if (Fs->cur_dv->fs_type != FSt_REDSEA) PrintErr("Not RedSea Drive\n"); else if (RedSeaFileFind(Fs->cur_dv, cur_dir_clus, name, &de, FUF_JUST_DIRS)) return TRUE; else PrintErr("File not found: \"%s\".\n", name); return FALSE; } U0 RedSeaFreeClus(CDrive *drive, I64 c, I64 count) { CFreeList *tmpf; Bool found = FALSE, unlock, unlock_break; DriveCheck(drive); if (!c) return; if (drive->fs_type != FSt_REDSEA) PrintErr("Not RedSea Drive\n"); else try { unlock_break = BreakLock; unlock = DriveLock(drive); if (!drive->next_free) RedSeaFreeListBuild(drive); tmpf = drive->next_free; while (!found && tmpf != &drive->next_free) { if (tmpf->start + tmpf->size == c) { tmpf->size += count; found = TRUE; } else if (c + count == tmpf->start) { tmpf->size += count; tmpf->start = c; found = TRUE; } tmpf = tmpf->next; } if (!found) { tmpf = ZMAlloc(sizeof(CFreeList)); tmpf->size = count; tmpf->start = c; QueueInsert(tmpf, drive->last_free); } while (count-- > 0) { DriveFATBlkSet(drive, c); LBtr(drive->cur_fat_blk, (c - drive->data_area) & (BLK_SIZE << 3 - 1)); LBts(&drive->fat_blk_dirty, 0); c++; } DriveFATBlkClean(drive); if (unlock) DriveUnlock(drive); if (unlock_break) BreakUnlock; } catch { if (unlock) DriveUnlock(drive); if (unlock_break) BreakUnlock; } } I64 RedSeaAllocClus(CDrive *drive, I64 count) { CFreeList *tmpf, *best_free = NULL; I64 i, first, best_size = I64_MAX; Bool unlock, unlock_break; if (count <= 0) throw('Drive'); try { unlock_break = BreakLock; unlock = DriveLock(drive); if (!drive->next_free) RedSeaFreeListBuild(drive); tmpf = drive->next_free; while (tmpf != &drive->next_free) { if (tmpf->size >= count && tmpf->size < best_size) { best_free = tmpf; best_size = tmpf->size; if (tmpf->size == count) break; } tmpf = tmpf->next; } if (!best_free) throw('Drive'); first = best_free->start; for (i = 0; i < count; i++) { DriveFATBlkSet(drive, first + i); LBts(drive->cur_fat_blk, (first + i - drive->data_area) & (BLK_SIZE << 3 - 1)); LBts(&drive->fat_blk_dirty, 0); } DriveFATBlkClean(drive); if (best_free->size -= count) best_free->start += count; else { QueueRemove(best_free); Free(best_free); } if (unlock) DriveUnlock(drive); if (unlock_break) BreakUnlock; } catch { if (unlock) DriveUnlock(drive); if (unlock_break) BreakUnlock; } return first; } Bool RedSeaDirNew(CDrive *drive, U8 *cur_dir, CDirEntry *tmpde, Bool free_old_chain) { CDirEntry *buf, *buf2, *ptr, de2; CRedSeaBoot *br; I64 c, ch, i = 1, j = 0, n = BLK_SIZE / CDIR_SIZE, dir_size, cur_dir_clus; Bool written = FALSE, unlock, unlock_break; U8 *tmp, *parent_dir; try { unlock_break = BreakLock; tmpde->attr |= RS_ATTR_CONTIGUOUS; unlock = DriveLock(drive); cur_dir_clus = Name2DirClus(drive, cur_dir); buf2 = MAlloc(BLK_SIZE); BlkRead(drive, buf2, cur_dir_clus, 1); ptr = buf2(U8 *) - offset(CDirEntry.start); buf = MAlloc(ptr->size); BlkRead(drive, buf, cur_dir_clus, ptr->size >> BLK_SIZE_BITS); dir_size = ptr->size; ptr = buf(U8 *) - offset(CDirEntry.start) + CDIR_SIZE; Free(buf2); while (TRUE) { if (!(ch = *ptr->name)) { if (!written) MemCopy(&ptr->start, &tmpde->start, CDIR_SIZE); if ((i + 1) * CDIR_SIZE + j << BLK_SIZE_BITS < dir_size) BlkWrite(drive, buf(U8 *) + j << BLK_SIZE_BITS, cur_dir_clus + j, 1); else { buf2 = CAlloc(dir_size + BLK_SIZE); MemCopy(buf2, buf, dir_size); RedSeaFreeClus(drive, cur_dir_clus, dir_size >> BLK_SIZE_BITS); dir_size += BLK_SIZE; c = ClusAlloc(drive, 0, dir_size >> BLK_SIZE_BITS, TRUE); Free(buf); buf = buf2; ptr = buf(U8 *) - offset(CDirEntry.start); ptr->size = dir_size; ptr->clus = c; BlkWrite(drive, buf, c, dir_size >> BLK_SIZE_BITS); if (cur_dir_clus == drive->root_clus) { br = CAlloc(BLK_SIZE); BlkRead(drive, br, drive->drv_offset, 1); br->root_clus = c; BlkWrite(drive, br, drive->drv_offset, 1); Free(br); drive->root_clus = c; } else { tmp = StrNew(cur_dir); parent_dir = StrNew(cur_dir); StrLastRemove(parent_dir, "/", tmp); if (!*parent_dir) { Free(parent_dir); parent_dir = StrNew("/"); } if (RedSeaFileFind(drive, Name2DirClus(drive, parent_dir), tmp, &de2, FUF_JUST_DIRS)) { de2.clus = c; de2.size = dir_size; RedSeaDirNew(drive, parent_dir, &de2, FALSE); } else throw('Drive'); Free(tmp); Free(parent_dir); } } break; } else if (ptr->attr & RS_ATTR_DELETED) { if (!written) { MemCopy(&ptr->start, &tmpde->start, CDIR_SIZE); BlkWrite(drive, buf(U8 *) + j << BLK_SIZE_BITS, cur_dir_clus + j, 1); written = TRUE; } } else { if (!StrCompare(tmpde->name, ptr->name)) { if (free_old_chain) RedSeaFreeClus(drive, ptr->clus, (ptr->size + BLK_SIZE - 1) >> BLK_SIZE_BITS); if (!written) MemCopy(&ptr->start, &tmpde->start, CDIR_SIZE); else ptr->attr |= RS_ATTR_DELETED; BlkWrite(drive, buf(U8 *) + j << BLK_SIZE_BITS, cur_dir_clus + j, 1); break; } } ptr(U8 *) += CDIR_SIZE; if (++i >= n) { j++; i = 0; } } Free(buf); if (unlock) DriveUnlock(drive); if (unlock_break) BreakUnlock; } catch { if (unlock) DriveUnlock(drive); if (unlock_break) BreakUnlock; } return FALSE; } I64 RedSeaFilesDel(CDrive *drive, U8 *cur_dir, U8 *files_find_mask, I64 fuf_flags, Bool del_dir, Bool print_message) { CDirEntry *buf, *buf2, *ptr; I64 i = 0, res = 0, ch, j = 0, n = BLK_SIZE / CDIR_SIZE, cur_dir_clus; Bool unlock_break; try { unlock_break = BreakLock; DriveLock(drive); cur_dir_clus = Name2DirClus(drive, cur_dir); buf2 = MAlloc(BLK_SIZE); BlkRead(drive, buf2, cur_dir_clus, 1); ptr = buf2(U8 *) - offset(CDirEntry.start); buf = MAlloc(ptr->size); BlkRead(drive, buf, cur_dir_clus, ptr->size >> BLK_SIZE_BITS); Free(buf2); ptr = buf(U8 *) - offset(CDirEntry.start); *ptr->name = '.'; ptr->name[1] = 0; while (TRUE) { if (!(ch = *ptr->name)) break; else if (!(ptr->attr & RS_ATTR_DELETED) && ch != '.' && (del_dir || !(ptr->attr & RS_ATTR_DIR)) && FilesFindMatch(ptr->name, files_find_mask, fuf_flags)) { if (!(ptr->attr & RS_ATTR_DIR)) res++; if (print_message) "Del %s\n", ptr->name; ptr->attr |= RS_ATTR_DELETED; BlkWrite(drive, buf(U8 *) + j << BLK_SIZE_BITS, cur_dir_clus + j, 1); RedSeaFreeClus(drive, ptr->clus, (ptr->size + BLK_SIZE - 1) >> BLK_SIZE_BITS); } ptr(U8 *) += CDIR_SIZE; if (++i >= n) { j++; i = 0; } } Free(buf); DriveUnlock(drive); if (unlock_break) BreakUnlock; } catch { DriveUnlock(drive); if (unlock_break) BreakUnlock; } return res; } I64 RedSeaFileWrite(CDrive *drive, U8 *cur_dir, U8 *name, U8 *buf, I64 size, CDate cdt, I64 attr) { CDirEntry de; I64 c = 0, blk_count; MemSet(&de, 0, sizeof(CDirEntry)); if (size < 0) size = 0; if (drive->fs_type != FSt_REDSEA) PrintErr("Not RedSea Drive\n"); else if (!CFileNameTo(de.name, name)) PrintErr("Invalid FileName: \"%s\".\n", name); else { RedSeaFilesDel(drive, cur_dir, de.name, 0, FALSE, FALSE); de.size = size; if (blk_count = (size + BLK_SIZE - 1) >> BLK_SIZE_BITS) c = ClusAlloc(drive, 0, blk_count, TRUE); //Always contiguous else c = INVALID_CLUS; de.clus = c; de.attr = attr|RS_ATTR_CONTIGUOUS; de.datetime = cdt; if (blk_count) BlkWrite(drive, buf, c, blk_count); RedSeaDirNew(drive, cur_dir, &de, TRUE); } return c; } CDirEntry *RedSeaFilesFind(U8 *files_find_mask, I64 fuf_flags, CDirEntry *parent=NULL) { CDrive *drive = Fs->cur_dv; CDirEntry *buf, *buf2, *ptr, *res = NULL, *tmpde; I64 ch, cur_dir_clus; if (fuf_flags & ~FUG_FILES_FIND) throw('FUF'); try { DriveLock(drive); cur_dir_clus = Name2DirClus(drive, Fs->cur_dir); buf2 = MAlloc(BLK_SIZE); BlkRead(drive, buf2, cur_dir_clus, 1); ptr = buf2(U8 *) - offset(CDirEntry.start); buf = MAlloc(ptr->size); BlkRead(drive, buf, cur_dir_clus, ptr->size >> BLK_SIZE_BITS); Free(buf2); ptr = buf(U8 *) - offset(CDirEntry.start); *ptr->name = '.'; ptr->name[1] = 0; ptr(U8 *) += CDIR_SIZE; ptr->clus = Name2ParentDirClus(drive, Fs->cur_dir); ptr(U8 *) -= CDIR_SIZE; while (TRUE) { if (!(ch = *ptr->name)) break; else if (!(ptr->attr & RS_ATTR_DELETED)) { tmpde = CAlloc(sizeof(CDirEntry)); MemCopy(&tmpde->start, &ptr->start, CDIR_SIZE); tmpde->parent = parent; if (Bt(&fuf_flags, FUf_RECURSE) && tmpde->attr & RS_ATTR_DIR && *tmpde->name != '.') { tmpde->next = res; res = tmpde; tmpde->full_name = DirNameAbs(tmpde->name); DriveUnlock(drive); if (Cd(tmpde->name)) { tmpde->sub = RedSeaFilesFind(files_find_mask, fuf_flags, tmpde); Cd(".."); } DriveLock(drive); } else { tmpde->full_name = FileNameAbs(tmpde->name); if ((tmpde->attr & RS_ATTR_DIR || !Bt(&fuf_flags, FUf_JUST_DIRS)) && !(Bt(&fuf_flags, FUf_RECURSE) && *tmpde->name == '.' && tmpde->attr & RS_ATTR_DIR) && FilesFindMatch(tmpde->full_name, files_find_mask, fuf_flags)) { tmpde->next = res; res = tmpde; } else DirEntryDel(tmpde); } } ptr(U8 *) += CDIR_SIZE; } Free(buf); DriveUnlock(drive); } catch DriveUnlock(drive); return res; } Bool RedSeaMkDir(CDrive *drive, U8 *cur_dir, U8 *name, I64 entry_count) {//entry_count is for preallocating dir blks. I64 c, cur_dir_clus = Name2DirClus(drive, cur_dir), size = CeilU64((entry_count + 3) << 6, BLK_SIZE); #assert CDIR_SIZE == 64 U8 *buf = CAlloc(size); CDirEntry *d_native = buf - offset(CDirEntry.start); Bool unlock_break; try { unlock_break = BreakLock; c = FileWrite(name, buf, size, 0, RS_ATTR_DIR); d_native->attr = RS_ATTR_DIR | RS_ATTR_CONTIGUOUS; StrCopy(d_native->name, name); d_native->clus = c; d_native->size = size; d_native->datetime = Now; d_native(U8 *) += CDIR_SIZE; d_native->attr = RS_ATTR_DIR | RS_ATTR_CONTIGUOUS; *d_native->name = '.'; d_native->name[1] = '.'; d_native->name[2] = 0; d_native->clus = cur_dir_clus; d_native->size = 0; d_native->datetime = Now; BlkWrite(drive, buf, c, 1); Free(buf); if (unlock_break) BreakUnlock; } catch if (unlock_break) BreakUnlock; return TRUE; }