import t, c from stdint import * import stdio import w32.win32base import w32.win32file class MODE(t.CEnum): R = 0 W = 1 A = 2 RP = 3 WP = 4 AP = 5 X = 6 XP = 7 class FRESULT(t.CEnum): OK = 0 ERR = -1 ERR_CLOSED = -2 ERR_PERM = -3 ERR_IO = -4 ERR_NOTFOUND = -5 SEEK_SET: t.CDefine = 0 SEEK_CUR: t.CDefine = 1 SEEK_END: t.CDefine = 2 INVALID_SET_FILE_POINTER: t.CDefine = -1 SHARE_READ: t.CDefine = 0x00000001 SHARE_WRITE: t.CDefine = 0x00000002 SHARE_DELETE: t.CDefine = 0x00000004 class File: handle: w32.win32base.HANDLE closed: bool can_read: bool can_write: bool is_append: bool _share_mode: ULONG def __init__(self, filename: str, mode: ULONG, share: ULONG = SHARE_READ): self.closed = True self.can_read = False self.can_write = False self.is_append = False self._share_mode = share access: ULONG = 0 disposition: ULONG = w32.win32file.OPEN_EXISTING if mode == MODE.R: access = w32.win32file.GENERIC_READ disposition = w32.win32file.OPEN_EXISTING self.can_read = True elif mode == MODE.W: access = w32.win32file.GENERIC_WRITE disposition = w32.win32file.CREATE_ALWAYS self.can_write = True elif mode == MODE.A: access = w32.win32file.GENERIC_WRITE disposition = w32.win32file.OPEN_ALWAYS self.can_write = True self.is_append = True elif mode == MODE.RP: access = w32.win32file.GENERIC_READ | w32.win32file.GENERIC_WRITE disposition = w32.win32file.OPEN_EXISTING self.can_read = True self.can_write = True elif mode == MODE.WP: access = w32.win32file.GENERIC_READ | w32.win32file.GENERIC_WRITE disposition = w32.win32file.CREATE_ALWAYS self.can_read = True self.can_write = True elif mode == MODE.AP: access = w32.win32file.GENERIC_READ | w32.win32file.GENERIC_WRITE disposition = w32.win32file.OPEN_ALWAYS self.can_read = True self.can_write = True self.is_append = True elif mode == MODE.X: access = w32.win32file.GENERIC_WRITE disposition = w32.win32file.CREATE_NEW self.can_write = True elif mode == MODE.XP: access = w32.win32file.GENERIC_READ | w32.win32file.GENERIC_WRITE disposition = w32.win32file.CREATE_NEW self.can_read = True self.can_write = True else: access = w32.win32file.GENERIC_READ disposition = w32.win32file.OPEN_EXISTING self.can_read = True self.handle = w32.win32file.CreateFileA( filename, access, self._share_mode, None, disposition, w32.win32file.FILE_ATTRIBUTE_NORMAL, None ) if self.handle == w32.win32base.INVALID_HANDLE_VALUE: self.closed = True return self.closed = False if self.is_append: self.seek(0, SEEK_END) def __enter__(self) -> 'File' | t.CPtr: return self def __exit__(self): self.close() def read(self, buf: bytes, count: ULONG) -> LONG: if self.closed: return FRESULT.ERR_CLOSED if not self.can_read: return FRESULT.ERR_PERM bytes_read: ULONG = 0 result: BOOL = w32.win32file.ReadFile(self.handle, buf, count, t.CPtr(c.Addr(bytes_read)), None) if result == 0: return FRESULT.ERR_IO return LONG(bytes_read) def write(self, buf: bytes, count: ULONG) -> LONG: if self.closed: return FRESULT.ERR_CLOSED if not self.can_write: return FRESULT.ERR_PERM bytes_written: ULONG = 0 result: BOOL = w32.win32file.WriteFile(self.handle, buf, count, t.CPtr(c.Addr(bytes_written)), None) if result == 0: return FRESULT.ERR_IO return LONG(bytes_written) def write_str(self, s: str) -> LONG: if self.closed: return FRESULT.ERR_CLOSED if not self.can_write: return FRESULT.ERR_PERM length: ULONG = 0 p: bytes = s while c.Deref(p) != 0: length += 1 p += 1 return self.write(s, length) def seek(self, offset: LONG, origin: ULONG) -> LONG: if self.closed: return FRESULT.ERR_CLOSED result: LONG = w32.win32file.SetFilePointer(self.handle, offset, None, origin) if result == -1: return FRESULT.ERR_IO return result def tell(self) -> LONG: if self.closed: return FRESULT.ERR_CLOSED return self.seek(0, SEEK_CUR) def close(self) -> LONG: if self.closed: return FRESULT.ERR_CLOSED w32.win32base.CloseHandle(self.handle) self.closed = True return FRESULT.OK def flush(self) -> LONG: if self.closed: return FRESULT.ERR_CLOSED w32.win32file.FlushFileBuffers(self.handle) return FRESULT.OK def size(self) -> LONGLONG: if self.closed: return -1 li: w32.win32base.LARGE_INTEGER = w32.win32base.LARGE_INTEGER() result: BOOL = w32.win32file.GetFileSizeEx(self.handle, t.CPtr(c.Addr(li))) if result == 0: return -1 return li.QuadPart def readline(self, buf: bytes, max_count: ULONG) -> LONG: if self.closed: return FRESULT.ERR_CLOSED if not self.can_read: return FRESULT.ERR_PERM if max_count < 2: return FRESULT.ERR total: ULONG = 0 bytes_read: ULONG = 0 ch: t.CInt = 0 while total < max_count - 1: bytes_read = 0 result: BOOL = w32.win32file.ReadFile(self.handle, buf + total, 1, t.CPtr(c.Addr(bytes_read)), None) if result == 0 or bytes_read == 0: break ch = c.Deref(buf + total) total += 1 if ch == 10: break if total < max_count: buf[total] = 0 return LONG(total) def read_all(self, buf: bytes, max_count: ULONG) -> LONG: if self.closed: return FRESULT.ERR_CLOSED if not self.can_read: return FRESULT.ERR_PERM if max_count < 2: return FRESULT.ERR # stdio.printf("[DBG read_all] before size()\n") file_size: LONGLONG = self.size() # stdio.printf("[DBG read_all] after size(), file_size=%lld\n", file_size) if file_size < 0: return FRESULT.ERR_IO to_read: ULONG = 0 if file_size < max_count - 1: to_read = ULONG(file_size) else: to_read = max_count - 1 # stdio.printf("[DBG read_all] to_read=%u, before seek\n", to_read) self.seek(0, SEEK_SET) # stdio.printf("[DBG read_all] after seek, before ReadFile\n") bytes_read: ULONG = 0 result: BOOL = w32.win32file.ReadFile(self.handle, buf, to_read, t.CPtr(c.Addr(bytes_read)), None) # stdio.printf("[DBG read_all] after ReadFile, result=%d, bytes_read=%u\n", result, bytes_read) if result == 0: return FRESULT.ERR_IO if bytes_read < max_count: buf[bytes_read] = 0 return LONG(bytes_read) class FileW: handle: w32.win32base.HANDLE closed: bool can_read: bool can_write: bool is_append: bool _share_mode: ULONG def __init__(self, filename: w32.win32base.LPCWSTR, mode: ULONG, share: ULONG = SHARE_READ): self.closed = True self.can_read = False self.can_write = False self.is_append = False self._share_mode = share access: ULONG = 0 disposition: ULONG = w32.win32file.OPEN_EXISTING if mode == MODE.R: access = w32.win32file.GENERIC_READ disposition = w32.win32file.OPEN_EXISTING self.can_read = True elif mode == MODE.W: access = w32.win32file.GENERIC_WRITE disposition = w32.win32file.CREATE_ALWAYS self.can_write = True elif mode == MODE.A: access = w32.win32file.GENERIC_WRITE disposition = w32.win32file.OPEN_ALWAYS self.can_write = True self.is_append = True elif mode == MODE.RP: access = w32.win32file.GENERIC_READ | w32.win32file.GENERIC_WRITE disposition = w32.win32file.OPEN_EXISTING self.can_read = True self.can_write = True elif mode == MODE.WP: access = w32.win32file.GENERIC_READ | w32.win32file.GENERIC_WRITE disposition = w32.win32file.CREATE_ALWAYS self.can_read = True self.can_write = True elif mode == MODE.AP: access = w32.win32file.GENERIC_READ | w32.win32file.GENERIC_WRITE disposition = w32.win32file.OPEN_ALWAYS self.can_read = True self.can_write = True self.is_append = True elif mode == MODE.X: access = w32.win32file.GENERIC_WRITE disposition = w32.win32file.CREATE_NEW self.can_write = True elif mode == MODE.XP: access = w32.win32file.GENERIC_READ | w32.win32file.GENERIC_WRITE disposition = w32.win32file.CREATE_NEW self.can_read = True self.can_write = True else: access = w32.win32file.GENERIC_READ disposition = w32.win32file.OPEN_EXISTING self.can_read = True self.handle = w32.win32file.CreateFileW( filename, access, self._share_mode, None, disposition, w32.win32file.FILE_ATTRIBUTE_NORMAL, None ) if self.handle == w32.win32base.INVALID_HANDLE_VALUE: self.closed = True return self.closed = False if self.is_append: self.seek(0, SEEK_END) def __enter__(self) -> 'FileW' | t.CPtr: return self def __exit__(self): self.close() def read(self, buf: bytes, count: ULONG) -> LONG: if self.closed: return FRESULT.ERR_CLOSED if not self.can_read: return FRESULT.ERR_PERM bytes_read: ULONG = 0 result: BOOL = w32.win32file.ReadFile(self.handle, buf, count, t.CPtr(c.Addr(bytes_read)), None) if result == 0: return FRESULT.ERR_IO return LONG(bytes_read) def write(self, buf: bytes, count: ULONG) -> LONG: if self.closed: return FRESULT.ERR_CLOSED if not self.can_write: return FRESULT.ERR_PERM bytes_written: ULONG = 0 result: BOOL = w32.win32file.WriteFile(self.handle, buf, count, t.CPtr(c.Addr(bytes_written)), None) if result == 0: return FRESULT.ERR_IO return LONG(bytes_written) def write_str(self, s: str) -> LONG: if self.closed: return FRESULT.ERR_CLOSED if not self.can_write: return FRESULT.ERR_PERM length: ULONG = 0 p: bytes = s while c.Deref(p) != 0: length += 1 p += 1 return self.write(s, length) def seek(self, offset: LONG, origin: ULONG) -> LONG: if self.closed: return FRESULT.ERR_CLOSED result: LONG = w32.win32file.SetFilePointer(self.handle, offset, None, origin) if result == -1: return FRESULT.ERR_IO return result def tell(self) -> LONG: if self.closed: return FRESULT.ERR_CLOSED return self.seek(0, SEEK_CUR) def close(self) -> LONG: if self.closed: return FRESULT.ERR_CLOSED w32.win32base.CloseHandle(self.handle) self.closed = True return FRESULT.OK def flush(self) -> LONG: if self.closed: return FRESULT.ERR_CLOSED w32.win32file.FlushFileBuffers(self.handle) return FRESULT.OK def size(self) -> LONGLONG: if self.closed: return -1 li: w32.win32base.LARGE_INTEGER = w32.win32base.LARGE_INTEGER() result: BOOL = w32.win32file.GetFileSizeEx(self.handle, t.CPtr(c.Addr(li))) if result == 0: return -1 return li.QuadPart def readline(self, buf: bytes, max_count: ULONG) -> LONG: if self.closed: return FRESULT.ERR_CLOSED if not self.can_read: return FRESULT.ERR_PERM if max_count < 2: return FRESULT.ERR total: ULONG = 0 bytes_read: ULONG = 0 ch: t.CInt = 0 while total < max_count - 1: bytes_read = 0 result: BOOL = w32.win32file.ReadFile(self.handle, buf + total, 1, t.CPtr(c.Addr(bytes_read)), None) if result == 0 or bytes_read == 0: break ch = c.Deref(buf + total) total += 1 if ch == 10: break if total < max_count: buf[total] = 0 return LONG(total) def read_all(self, buf: bytes, max_count: ULONG) -> LONG: if self.closed: return FRESULT.ERR_CLOSED if not self.can_read: return FRESULT.ERR_PERM if max_count < 2: return FRESULT.ERR file_size: LONGLONG = self.size() if file_size < 0: return FRESULT.ERR_IO to_read: ULONG = 0 if file_size < max_count - 1: to_read = ULONG(file_size) else: to_read = max_count - 1 self.seek(0, SEEK_SET) bytes_read: ULONG = 0 result: BOOL = w32.win32file.ReadFile(self.handle, buf, to_read, t.CPtr(c.Addr(bytes_read)), None) if result == 0: return FRESULT.ERR_IO if bytes_read < max_count: buf[bytes_read] = 0 return LONG(bytes_read) def open(filename: str, mode: ULONG, share: ULONG = SHARE_READ) -> File | t.CPtr: return File(filename, mode, share) def openw(filename: LPCWSTR, mode: ULONG, share: ULONG = SHARE_READ) -> FileW | t.CPtr: return FileW(filename, mode, share)