FILESYSTEM = "fs.img" with open(FILESYSTEM, "rb") as f: fs = f.read() class ByteArray: def __init__(self, data = b''): self.data = data self.read = 0 def __getitem__(self, idx): return self.data[idx] def __repr__(self): return "0x" + "".join([hex(i)[2:].upper().zfill(2) for i in self.data])+ " (" + repr("".join([chr(i) for i in self.data])) + ")" def __int__(self): return int.from_bytes(self.data, byteorder='little', signed=False) def __str__(self): return self.cutws(str(repr("".join([chr(i) for i in self.data]))[1:-1])) def __add__(self, other): return ByteArray(self.data + other.data) def CRW(self): ret = self.data[self.read:self.read+2] self.read += 2 return ByteArray(ret) def CRB(self): ret = self.data[self.read:self.read+1] self.read += 1 return ByteArray(ret) def CR(self, size): ret = ByteArray(self.data[self.read:self.read+size]) self.read += size return ret def RW(self, offset = None): return self.RR(2,offset) def RB(self, offset = None): return self.RR(1, offset) def RR(self, size, offset = None): if not offset: offset = self.read return ByteArray(self.data[offset:offset+size]) def cutws(self, a): l = -1 for n, i in reversed(list(enumerate(a))): if i != " ": l = n break return a[:l+1] FS = ByteArray(fs) FAT_DATA = { "BJI": FS.CR(0x3), "OEM": FS.CR(0x8) } BPB = FS.CR(13) FAT_RESERVED_DATA = { "BytePerLogSect": BPB.CRW(), "LogSectPerClus": BPB.CRB(), "ReservedLogSect": BPB.CRW(), "NumFAT": BPB.CRB(), "RootDE": BPB.CRW(), "TotalLogSect": BPB.CRW(), "MediaDesc": BPB.CRB(), "LogSectPerFAT": BPB.CRW(), } FAT_BYTE_PER_SECTOR = int(FAT_RESERVED_DATA["BytePerLogSect"]) FAT_SECT_PER_CLUS = int(FAT_RESERVED_DATA["LogSectPerClus"]) FAT_RESERVED_SECTOR_COUNT = int(FAT_RESERVED_DATA["ReservedLogSect"]) FAT_FAT_COUNT = int(FAT_RESERVED_DATA["NumFAT"]) FAT_SECTOR_PER_FAT = int(FAT_RESERVED_DATA["LogSectPerFAT"]) FAT_ROOT_DIRENT = int(FAT_RESERVED_DATA["RootDE"]) FAT_SECT_COUNT = int(FAT_RESERVED_DATA["TotalLogSect"]) FAT_BYTE_PER_CLUS = FAT_BYTE_PER_SECTOR * FAT_SECT_PER_CLUS FAT_RESERVED_SECTOR_SIZE = FAT_BYTE_PER_SECTOR * FAT_RESERVED_SECTOR_COUNT BPB = FS.CR(FAT_RESERVED_SECTOR_SIZE - 24) FAT_RESERVED_DATA.update({ # DOS 3.31 "PSpT": BPB.CRW(), "NH": BPB.CRW(), "HiddenSect": BPB.CR(4), "LTotalLogSect": BPB.CR(4), # DOS 4.0 "PDN": BPB.CRB(), "FLG": BPB.CRB(), "EBS": BPB.CRB(), "VolumeSN": BPB.CR(4), "Label": BPB.CR(11), "FSType": BPB.CR(8) }) print(f"""VOLUME \"{str(FAT_RESERVED_DATA["Label"])}\" TYPE {str(FAT_RESERVED_DATA["FSType"])} - B/S: {FAT_BYTE_PER_SECTOR} - S/C: {FAT_SECT_PER_CLUS} - FAT: {FAT_FAT_COUNT} - S/F: {FAT_SECTOR_PER_FAT} - RDE: {FAT_ROOT_DIRENT} - TSC: {FAT_SECT_COUNT} - BPC: {FAT_BYTE_PER_CLUS} - RSC: {FAT_RESERVED_SECTOR_COUNT} """) #print(FAT_FAT_COUNT, FAT_SECTOR_PER_FAT, FAT_BYTE_PER_SECTOR, \ # FAT_SECT_PER_CLUS, FAT_SECTOR_PER_FAT) FAT = FS.CR(FAT_SECTOR_PER_FAT * FAT_BYTE_PER_SECTOR * FAT_FAT_COUNT) FAT_TABLE = [] for _ in range(FAT_FAT_COUNT): FAT_TABLE.append(FAT.CR(FAT_SECTOR_PER_FAT * FAT_BYTE_PER_SECTOR)) # print(FAT_ROOT_DIRENT) def PDE(DE): assert len(DE.data) == 32 FILE = { "DIR_NAME": DE.CR(8), "DIR_EXT": DE.CR(3), "DIR_ATTR": DE.CRB(), "DIR_NTR": DE.CRB(), "DIR_CRT": DE.CRB(), "DIR_CRT": DE.CRW(), "DIR_CRD": DE.CRW(), "DIR_LAD": DE.CRW(), "DIR_FCNH": DE.CRW(), "DIR_WT": DE.CRW(), "DIR_WD": DE.CRW(), "DIR_FCNL": DE.CRW(), "DIR_FS": DE.CR(4) } if int(FILE["DIR_NAME"].RB()) == 0: return 0 if int(FILE["DIR_NAME"].RB()) == 0xE5: return -1 FILE["DIR_FCN"] = FILE["DIR_FCNL"] + FILE["DIR_FCNH"] return FILE def FILE_CONTENT(DATA, FILE): FILE_CIDX = FILE["FAT_CLUS"] FILE_CONTENT = ByteArray() for FILE_IDX in FILE_CIDX: # print(FILE_IDX) FILE_CONTENT += DATA.RR(FAT_BYTE_PER_CLUS, FAT_BYTE_PER_CLUS * (FILE_IDX) ) return FILE_CONTENT def GET_DIR_CHLD(DATA, FILE): if int(FILE['DIR_ATTR']) != 16: print("Not Directory.") return -1 FC = FILE_CONTENT(DATA, FILE) FDR = [] while (FDE := FC.CR(32)).data: if (FDF := PDE(FDE)): FDR.append(FDF) return FDR def PRINT_DIR(DE): FILE_NAME = str(DE["DIR_NAME"]) + (("." + str(DE["DIR_EXT"])) if str(DE["DIR_EXT"]) else "") FILE_TYPE = "f" if int(DE["DIR_ATTR"]) == 32 else "d" if int(DE["DIR_ATTR"]) == 16 else "." FILE_WT = int(DE["DIR_WT"]) FILE_SIZE = int(DE["DIR_FS"]) print(f"{FILE_TYPE} {FILE_NAME:<11} {FILE_SIZE:>6d}B {FILE_WT}") def LIST_DIR(DATA, DIRENT): print() print(f"Child of {str(DIRENT["DIR_NAME"])}") print(f"{len(FILES := GET_DIR_CHLD(DATA, DIRENT))}") for FILE in FILES: PRINT_DIR(FILE) print() return FILES def FIND_FAT(FAT_TABLE, FAT_IDX): # FAT_IDX = int(FILE["DIR_FCN"]) FAT_CLUS = [] while True: FAT_CLUS.append(FAT_IDX - 2) FAT_IDX = FAT_TABLE[0][ FAT_IDX * 2 ] if int(FAT_IDX) == 255: break # print(FAT_CLUS) return FAT_CLUS ISDIR = lambda FILE: int(FILE["DIR_ATTR"]) == 16 RDR = FS.CR(FAT_ROOT_DIRENT * 32) DATA = FS.CR(FAT_SECT_COUNT * FAT_BYTE_PER_SECTOR - FS.read) RDE = {"DIR_NAME": "/", "FAT_CLUS": [0], "DIR_ATTR": 16} for F in (RDS := LIST_DIR(RDR, RDE)): F["FAT_CLUS"] = FIND_FAT(FAT_TABLE, int(F["DIR_FCN"])) if ISDIR(F): LIST_DIR(DATA, F) def GET_FILE(DATA, PATH): CFP = RDS.copy() for FP in (PS := PATH.split("/"))[1:-1]: print(FP) for F in CFP: if str(F["DIR_NAME"]) == FP and ISDIR(F): CFP = GET_DIR_CHLD(DATA, F) break for F in CFP: if f"{str(F["DIR_NAME"])}.{str(F["DIR_EXT"])}" == PS[-1]: F["FAT_CLUS"] = FIND_FAT(FAT_TABLE, int(F["DIR_FCN"])) return FILE_CONTENT(DATA, F) import os, sys while True: FP = input("? ") if os.path.exists(FP.split("/")[-1]): exit() with open(FP.split("/")[-1], "wb") as f: f.write(GET_FILE(DATA, FP).data)