Copy // Some code
import struct
import win32file
import subprocess
import tkinter as tk
from tkinter import ttk, simpledialog, messagebox
import os
selected_drive = None
root_dir_sector = None
sectors_per_cluster = None
def list_physical_drives():
drives = []
powershell_cmd = [
"powershell",
"-Command",
"Get-WmiObject Win32_DiskDrive | Select-Object DeviceID,Model"
]
result = subprocess.run(powershell_cmd, stdout=subprocess.PIPE, text=True)
lines = result.stdout.strip().split('\n')[2:] # 첫 두 줄 제거 (헤더 + 공백)
for line in lines:
if line.strip():
parts = line.strip().split(None, 1)
if len(parts) == 2:
device_id = parts[0]
model = parts[1]
drives.append((device_id, model))
return drives
def open_drive(drive, access_mode):
return win32file.CreateFile(drive, access_mode, win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE, None, win32file.OPEN_EXISTING, 0, None)
def read_mbr(drive):
h = open_drive(drive, win32file.GENERIC_READ)
mbr = win32file.ReadFile(h, 512)[1]
h.close()
return mbr
def parse_partitions(mbr):
partitions = []
for i in range(4):
offset = 446 + i * 16
part_entry = mbr[offset:offset + 16]
boot_flag, start_chs, part_type, end_chs, start_lba, size = struct.unpack('<B3sB3sII', part_entry)
if part_type == 0x0B or part_type == 0x0C: # FAT32
partitions.append({'start_lba': start_lba, 'size': size})
return partitions
def read_sector(drive, sector):
try:
h = open_drive(drive, win32file.GENERIC_READ)
win32file.SetFilePointer(h, sector * 512, win32file.FILE_BEGIN)
data = win32file.ReadFile(h, 512)[1]
h.close()
print(f"Successfully read sector {sector}")
return data
except Exception as e:
print(f"Error reading sector {sector}: {e}")
def write_directory_entry(drive, sector, entry_offset, entry_data):
try:
h = open_drive(drive, win32file.GENERIC_WRITE)
win32file.SetFilePointer(h, sector * 512 + entry_offset, win32file.FILE_BEGIN)
win32file.WriteFile(h, entry_data)
h.close()
print(f"Successfully wrote directory entry to sector {sector}")
except Exception as e:
print(f"Error writing directory entry to sector {sector}: {e}")
def write_sector(drive, sector, data):
print('data len before writing:', len(data))
try:
if len(data) != 512:
raise ValueError("Data length must be exactly 512 bytes.")
h = open_drive(drive, win32file.GENERIC_WRITE)
win32file.SetFilePointer(h, sector * 512, win32file.FILE_BEGIN)
win32file.WriteFile(h, data)
h.close()
print(f"Successfully wrote sector {sector}")
except Exception as e:
print(f"Error writing sector {sector}: {e}")
def log_error(sector, data, error):
with open("write_error_log.txt", "a") as log_file:
log_file.write(f"Error writing to sector {sector}: {error}\n")
log_file.write(f"Data: {data.hex()}\n")
log_file.write("----\n")
def read_fat32_root(drive, start_lba):
boot_sector = read_sector(drive, start_lba)
bytes_per_sector = struct.unpack_from('<H', boot_sector, 11)[0]
global sectors_per_cluster
sectors_per_cluster = struct.unpack_from('<B', boot_sector, 13)[0]
reserved_sectors = struct.unpack_from('<H', boot_sector, 14)[0]
number_of_fats = struct.unpack_from('<B', boot_sector, 16)[0]
fat_size = struct.unpack_from('<I', boot_sector, 36)[0]
root_dir_sector = start_lba + reserved_sectors + number_of_fats * fat_size
root_dir_data = bytearray()
current_cluster = 2
while True:
for i in range(sectors_per_cluster):
sector = root_dir_sector + (current_cluster - 2) * sectors_per_cluster + i
cluster_data = read_sector(drive, sector)
root_dir_data.extend(cluster_data)
next_cluster = read_next_cluster(drive, start_lba, current_cluster, reserved_sectors, fat_size)
if next_cluster is None: # Check for the end of the chain
break
current_cluster = next_cluster
return root_dir_data, root_dir_sector, bytes_per_sector, sectors_per_cluster, reserved_sectors, fat_size
def read_next_cluster(drive, start_lba, cluster, reserved_sectors, fat_size):
fat_start_sector = start_lba + reserved_sectors
fat_offset = cluster * 4
fat_sector = fat_start_sector + (fat_offset // 512)
fat_entry_offset = fat_offset % 512
fat_sector_data = read_sector(drive, fat_sector)
# Print the raw FAT sector data for debugging
print(f"Reading FAT sector: {fat_sector}, FAT entry offset: {fat_entry_offset}")
fat_entry_data = fat_sector_data[fat_entry_offset:fat_entry_offset+4]
print(f"FAT entry data (hex): {fat_entry_data.hex()}")
next_cluster = struct.unpack_from('<I', fat_entry_data)[0] & 0x0FFFFFFF
# Check if next_cluster indicates the end of the cluster chain
if next_cluster >= 0x0FFFFFF8:
print(f"Next cluster: End of chain (0x{next_cluster:X})")
return None # Indicate the end of the chain
print(f"Next cluster: {next_cluster}")
return next_cluster
def parse_directory_entries(directory_data):
entries = []
lfn_entries = []
for i in range(0, len(directory_data), 32):
entry = directory_data[i:i+32]
if entry[0] == 0x00:
break
attrs = entry[11]
if attrs == 0x0F: # LFN entry
lfn_entries.append(entry)
else:
if lfn_entries:
lfn_entries.reverse()
lfn_name = ''.join(
[lfn_entry[1:11].decode('utf-16le', errors='ignore').rstrip('\x00').rstrip() +
lfn_entry[14:26].decode('utf-16le', errors='ignore').rstrip('\x00').rstrip() +
lfn_entry[28:32].decode('utf-16le', errors='ignore').rstrip('\x00').rstrip()
for lfn_entry in lfn_entries]
).rstrip()
lfn_entries = []
else:
lfn_name = entry[0:11].decode('ascii', errors='ignore').strip()
is_deleted = entry[0] == 0xE5
file_size = struct.unpack_from('<I', entry, 28)[0]
start_cluster_lo = struct.unpack_from('<H', entry, 26)[0]
start_cluster_hi = struct.unpack_from('<H', entry, 20)[0]
start_cluster = (start_cluster_hi << 16) + start_cluster_lo
is_directory = (attrs & 0x10) != 0
display_name = f"{lfn_name} (삭제됨)" if is_deleted else lfn_name
entries.append({
'name': display_name,
'is_deleted': is_deleted,
'is_directory': is_directory,
'size': file_size,
'start_cluster': start_cluster,
'raw_entry': entry,
'entry_offset': i
})
return entries
def display_directory_structure(tree, parent, entries):
for entry in entries:
if entry['is_directory']:
node = tree.insert(parent, 'end', text=entry['name'], values=(entry['start_cluster'],), open=True)
tree.insert(node, 'end', text='dummy')
else:
tree.insert(parent, 'end', text=entry['name'], values=(entry['start_cluster'],))
def on_folder_select(event, tree, file_tree, drive, root_dir_sector, details_text, data_text, start_lba, reserved_sectors, fat_size):
selected_item = tree.selection()[0]
selected_values = tree.item(selected_item, 'values')
if not selected_values:
return
selected_value = selected_values[0]
# 논리 드라이브 경로 처리
if ':' in selected_value:
selected_path = selected_value
# 디렉토리 엔트리 파싱
entries = []
for item in os.listdir(selected_path):
item_path = os.path.join(selected_path, item)
is_directory = os.path.isdir(item_path)
if not is_directory:
entries.append({
'name': item,
'is_directory': is_directory,
'path': item_path,
'is_deleted': False,
'size': os.path.getsize(item_path) if not is_directory else '',
'start_cluster': '', # 논리 드라이브에서는 클러스터 정보를 사용하지 않음
'raw_entry': '',
'entry_offset': ''
})
# 파일 목록 업데이트
for i in file_tree.get_children():
file_tree.delete(i)
for entry in entries:
file_tree.insert('', 'end', values=(entry['name'], entry['size'], entry['start_cluster'], 'No'))
# 파일 선택 시 세부 정보 표시
def on_file_select(event):
selected_file = file_tree.selection()[0]
selected_entry = file_tree.item(selected_file, 'values')
entry_data = next(e for e in entries if e['name'] == selected_entry[0])
details_text.delete(1.0, tk.END)
details_text.insert(tk.END, f"Name: {selected_entry[0]}\n")
details_text.insert(tk.END, f"Size: {selected_entry[1]}\n")
details_text.insert(tk.END, f"Path: {entry_data['path']}\n")
data_text.delete(1.0, tk.END)
try:
with open(entry_data['path'], 'r') as f:
data_text.insert(tk.END, f.read())
except Exception as e:
data_text.insert(tk.END, f"Error reading file: {e}")
file_tree.bind('<<TreeviewSelect>>', on_file_select)
# 물리 드라이브 처리
else:
try:
selected_cluster = int(selected_value)
except ValueError:
return
directory_data = bytearray()
current_cluster = selected_cluster
while True:
for i in range(sectors_per_cluster):
sector = root_dir_sector + (current_cluster - 2) * sectors_per_cluster + i
cluster_data = read_sector(drive, sector)
directory_data.extend(cluster_data)
next_cluster = read_next_cluster(drive, start_lba, current_cluster, reserved_sectors, fat_size)
if next_cluster is None: # Check for the end of the chain
break
current_cluster = next_cluster
entries = parse_directory_entries(directory_data)
for i in file_tree.get_children():
file_tree.delete(i)
for entry in entries:
file_tree.insert('', 'end', values=(entry['name'], entry['size'], entry['start_cluster'], 'Yes' if entry['is_deleted'] else 'No'))
def on_file_select(event):
selected_file = file_tree.selection()[0]
selected_entry = file_tree.item(selected_file, 'values')
entry_data = next(e for e in entries if e['name'] == selected_entry[0])
entry_sector = root_dir_sector + (entry_data['start_cluster'] - 2) * sectors_per_cluster
entry_offset = entry_data['entry_offset']
details_text.delete(1.0, tk.END)
details_text.insert(tk.END, f"Name: {selected_entry[0]}\n")
details_text.insert(tk.END, f"Size: {selected_entry[1]}\n")
details_text.insert(tk.END, f"Start Cluster: {selected_entry[2]}\n")
details_text.insert(tk.END, f"Deleted: {selected_entry[3]}\n")
details_text.insert(tk.END, f"Raw Entry: {entry_data['raw_entry']}\n")
details_text.insert(tk.END, f"Sector Number: {entry_sector}\n")
details_text.insert(tk.END, f"Entry Offset: {entry_offset}\n")
data_text.delete(1.0, tk.END)
if entry_data['is_directory']:
directory_data = bytearray()
current_cluster = entry_data['start_cluster']
while True:
for i in range(sectors_per_cluster):
sector = root_dir_sector + (current_cluster - 2) * sectors_per_cluster + i
cluster_data = read_sector(drive, sector)
directory_data.extend(cluster_data)
next_cluster = read_next_cluster(drive, start_lba, current_cluster, reserved_sectors, fat_size)
if next_cluster is None: # Check for the end of the chain
break
current_cluster = next_cluster
data_text.insert(tk.END, directory_data)
else:
sector_data = read_sector(drive, entry_sector)
data_text.insert(tk.END, sector_data)
file_tree.bind('<<TreeviewSelect>>', on_file_select)
def create_logical_file(tree, logical_drive):
selected_item = tree.selection()[0]
selected_path = tree.item(selected_item, 'values')[0]
file_name = simpledialog.askstring("File Name", "Enter the new file name:")
if file_name:
file_path = os.path.join(selected_path, file_name)
def save_file():
content = text.get(1.0, tk.END)
try:
with open(file_path, 'w') as f:
f.write(content)
print(f"File {file_name} created successfully at {selected_path}")
display_logical_directory(tree, logical_drive)
new_window.destroy()
except Exception as e:
messagebox.showerror("Error", f"Failed to create file: {e}")
new_window = tk.Toplevel()
new_window.title("Edit File Content")
text = tk.Text(new_window, width=80, height=20)
text.pack()
save_button = tk.Button(new_window, text="Save", command=save_file)
save_button.pack()
def create_logical_folder(tree, logical_drive):
selected_item = tree.selection()[0]
selected_path = tree.item(selected_item, 'values')[0]
folder_name = simpledialog.askstring("Folder Name", "Enter the new folder name:")
if folder_name:
folder_path = os.path.join(selected_path, folder_name)
try:
os.makedirs(folder_path)
print(f"Folder {folder_name} created successfully at {selected_path}")
display_logical_directory(tree, logical_drive)
except Exception as e:
messagebox.showerror("Error", f"Failed to create folder: {e}")
def delete_logical_file(tree, logical_drive):
selected_item = tree.selection()[0]
selected_path = tree.item(selected_item, 'values')[0]
selected_file_item = logical_file_tree.selection()
if selected_file_item:
selected_file = logical_file_tree.item(selected_file_item[0], 'values')[0]
file_path = os.path.join(selected_path, selected_file)
try:
os.remove(file_path)
print(f"File {file_path} deleted successfully.")
display_logical_directory(tree, logical_drive)
except Exception as e:
messagebox.showerror("Error", f"Failed to delete file: {e}")
else:
if os.path.isdir(selected_path):
if os.listdir(selected_path):
messagebox.showerror("Error", "Cannot delete a non-empty directory.")
else:
try:
os.rmdir(selected_path)
print(f"Folder {selected_path} deleted successfully.")
display_logical_directory(tree, logical_drive)
except Exception as e:
messagebox.showerror("Error", f"Failed to delete folder: {e}")
def edit_logical_file(tree, logical_drive):
selected_item = tree.selection()[0]
selected_path = tree.item(selected_item, 'values')[0]
selected_file_item = logical_file_tree.selection()
if not selected_file_item:
messagebox.showerror("Error", "No file selected to edit.")
return
selected_file = logical_file_tree.item(selected_file_item[0], 'values')[0]
file_path = os.path.join(selected_path, selected_file)
if not os.path.isdir(file_path):
def save_changes():
content = text.get(1.0, tk.END)
try:
with open(file_path, 'w') as f:
f.write(content)
print(f"File {file_path} edited successfully.")
display_logical_directory(tree, logical_drive)
edit_window.destroy()
except Exception as e:
messagebox.showerror("Error", f"Failed to edit file: {e}")
edit_window = tk.Toplevel()
edit_window.title("Edit File")
text = tk.Text(edit_window, width=80, height=20)
text.pack()
save_button = tk.Button(edit_window, text="Save", command=save_changes)
save_button.pack()
try:
with open(file_path, 'r') as f:
text.insert(tk.END, f.read())
except Exception as e:
messagebox.showerror("Error", f"Failed to read file: {e}")
def list_logical_drives():
drives = [f"{chr(d)}:\\" for d in range(65, 91) if os.path.exists(f"{chr(d)}:\\")]
drives = [drive for drive in drives if drive != "C:\\"] # C 드라이브 제외
return drives
def display_logical_directory(tree, path):
for i in tree.get_children():
tree.delete(i)
node = tree.insert('', 'end', text=path, values=(path,), open=True)
_display_logical_subdirectory(tree, node, path)
def _display_logical_subdirectory(tree, parent, path):
try:
for item in os.listdir(path):
abs_path = os.path.join(path, item)
if os.path.isdir(abs_path):
node = tree.insert(parent, 'end', text=item, values=(abs_path,))
_display_logical_subdirectory(tree, node, abs_path)
except Exception as e:
messagebox.showerror("Error", f"Failed to display directory: {e}")
def on_logical_tree_open(event, tree):
item = tree.selection()[0]
path = tree.item(item, 'values')[0]
children = tree.get_children(item)
if len(children) == 1 and tree.item(children[0], 'text') == 'dummy':
tree.delete(children[0])
_display_logical_subdirectory(tree, item, path)
def on_physical_tree_open(event, tree, file_tree, drive, root_dir_sector, details_text, data_text, start_lba, reserved_sectors, fat_size):
selected_item = tree.selection()[0]
selected_values = tree.item(selected_item, 'values')
if not selected_values:
return
selected_cluster = int(selected_values[0])
directory_data = bytearray()
current_cluster = selected_cluster
while True:
for i in range(sectors_per_cluster):
sector = root_dir_sector + (current_cluster - 2) * sectors_per_cluster + i
cluster_data = read_sector(drive, sector)
directory_data.extend(cluster_data)
next_cluster = read_next_cluster(drive, start_lba, current_cluster, reserved_sectors, fat_size)
if next_cluster is None: # Check for the end of the chain
break
current_cluster = next_cluster
entries = parse_directory_entries(directory_data)
for i in tree.get_children(selected_item):
tree.delete(i)
display_directory_structure(tree, selected_item, entries)
update_file_list(entries, file_tree, details_text, data_text)
def update_file_list(entries, file_tree, details_text, data_text):
for i in file_tree.get_children():
file_tree.delete(i)
for entry in entries:
file_tree.insert('', 'end', values=(entry['name'], entry['size'], entry['start_cluster'], 'Yes' if entry['is_deleted'] else 'No'))
def on_file_select(event):
selected_file = file_tree.selection()[0]
selected_entry = file_tree.item(selected_file, 'values')
entry_data = next(e for e in entries if e['name'] == selected_entry[0])
details_text.delete(1.0, tk.END)
details_text.insert(tk.END, f"Name: {selected_entry[0]}\n")
details_text.insert(tk.END, f"Size: {selected_entry[1]}\n")
details_text.insert(tk.END, f"Start Cluster: {selected_entry[2]}\n")
details_text.insert(tk.END, f"Deleted: {selected_entry[3]}\n")
details_text.insert(tk.END, f"Raw Entry: {entry_data['raw_entry']}\n")
data_text.delete(1.0, tk.END)
if entry_data['is_directory']:
data_text.insert(tk.END, "[Directory content]")
else:
data_text.insert(tk.END, "[File content]")
file_tree.bind('<<TreeviewSelect>>', on_file_select)
def on_logical_folder_select(event, tree, logical_file_tree, logical_details_text, logical_data_text):
selected_item = tree.selection()[0]
selected_path = tree.item(selected_item, 'values')[0]
entries = []
for item in os.listdir(selected_path):
item_path = os.path.join(selected_path, item)
is_directory = os.path.isdir(item_path)
entries.append({
'name': item,
'is_directory': is_directory,
'path': item_path,
'is_deleted': False,
'size': os.path.getsize(item_path) if not is_directory else '',
'start_cluster': '', # 논리 드라이브에서는 클러스터 정보를 사용하지 않음
'raw_entry': '',
'entry_offset': ''
})
for i in logical_file_tree.get_children():
logical_file_tree.delete(i)
for entry in entries:
logical_file_tree.insert('', 'end', values=(entry['name'], entry['size'], entry['start_cluster'], 'No'))
def on_logical_file_select(event):
selected_file = logical_file_tree.selection()[0]
selected_entry = logical_file_tree.item(selected_file, 'values')
entry_data = next(e for e in entries if e['name'] == selected_entry[0])
logical_details_text.delete(1.0, tk.END)
logical_details_text.insert(tk.END, f"Name: {selected_entry[0]}\n")
logical_details_text.insert(tk.END, f"Size: {selected_entry[1]}\n")
logical_details_text.insert(tk.END, f"Path: {entry_data['path']}\n")
logical_data_text.delete(1.0, tk.END)
try:
with open(entry_data['path'], 'r') as f:
logical_data_text.insert(tk.END, f.read())
except Exception as e:
logical_data_text.insert(tk.END, f"Error reading file: {e}")
logical_file_tree.bind('<<TreeviewSelect>>', on_logical_file_select)
def main():
global selected_drive, root_dir_sector, sectors_per_cluster, start_lba, reserved_sectors, fat_size, file_tree, logical_file_tree, selected_drive_label
drives = list_physical_drives()
if not drives:
print("No physical drives found.")
messagebox.showerror("Error", "No physical drives found.")
return
logical_drives = list_logical_drives()
if not logical_drives:
print("No logical drives found.")
messagebox.showerror("Error", "No logical drives found.")
return
print("Available drives:", drives)
root = tk.Tk()
root.title("FAT32 Explorer")
root.geometry("1100x800") # 전체 창 크기를 1100x800으로 설정
# Top frame for drive selections
top_frame = tk.Frame(root)
top_frame.pack(side=tk.TOP, fill=tk.X)
# Physical drive selection frame (left half)
physical_drive_frame = tk.Frame(top_frame, width=550)
physical_drive_frame.pack(side=tk.LEFT, fill=tk.X)
lbl = tk.Label(physical_drive_frame, text="물리디스크 선택:")
lbl.pack(side=tk.LEFT, padx=5, pady=5)
drive_var = tk.StringVar(physical_drive_frame)
drive_info = [f"{drive} - {model}" for drive, model in drives]
drive_var.set(drive_info[0]) # Default value
drive_menu = tk.OptionMenu(physical_drive_frame, drive_var, *drive_info)
drive_menu.pack(side=tk.LEFT, padx=5, pady=5)
btn = tk.Button(physical_drive_frame, text="물리드라이브 선택", command=lambda: on_select())
btn.pack(side=tk.LEFT, padx=5, pady=5)
# Logical drive selection frame (right half)
logical_drive_frame = tk.Frame(top_frame, width=550)
logical_drive_frame.pack(side=tk.RIGHT, fill=tk.X)
logical_drive_lbl = tk.Label(logical_drive_frame, text="논리드라이브 선택:")
logical_drive_lbl.pack(side=tk.LEFT, padx=5, pady=5)
logical_drive_var = tk.StringVar(logical_drive_frame)
logical_drive_var.set(logical_drives[0]) # Default value
logical_drive_menu = tk.OptionMenu(logical_drive_frame, logical_drive_var, *logical_drives)
logical_drive_menu.pack(side=tk.LEFT, padx=5, pady=5)
logical_drive_btn = tk.Button(logical_drive_frame, text="논리드라이브 선택", command=lambda: display_logical_directory(logical_tree, logical_drive_var.get()))
logical_drive_btn.pack(side=tk.LEFT, padx=5, pady=5)
# Splitter frame
splitter_frame = tk.PanedWindow(root, orient=tk.HORIZONTAL)
splitter_frame.pack(fill=tk.BOTH, expand=True)
frame_width = int(1100 / 4) # 4등분한 각 프레임의 너비
# Left tree view (physical drive structure)
physical_frame = tk.Frame(splitter_frame, width=frame_width)
splitter_frame.add(physical_frame, stretch="always")
physical_tree = ttk.Treeview(physical_frame, columns=('Start Cluster'), show='tree')
physical_tree.pack(fill=tk.BOTH, expand=True)
# Center file list and details frame
center_frame = tk.Frame(splitter_frame, width=frame_width)
splitter_frame.add(center_frame, stretch="always")
# File list tree view
file_tree = ttk.Treeview(center_frame, columns=('Name', 'Size', 'Start Cluster', 'Deleted'), show='headings')
file_tree.heading('Name', text='Name')
file_tree.heading('Size', text='Size')
file_tree.heading('Start Cluster', text='Start Cluster')
file_tree.heading('Deleted', text='Deleted')
file_tree.pack(fill=tk.BOTH, expand=True)
# File details and data frames
details_frame = tk.Frame(center_frame, width=frame_width, height=300)
details_frame.pack(fill=tk.BOTH, expand=True)
details_text = tk.Text(details_frame, height=10)
details_text.pack(fill=tk.BOTH, expand=True)
data_text = tk.Text(details_frame, height=10)
data_text.pack(fill=tk.BOTH, expand=True)
# Right frame (logical drive structure)
logical_frame = tk.Frame(splitter_frame, width=frame_width)
splitter_frame.add(logical_frame, stretch="always")
logical_tree = ttk.Treeview(logical_frame, columns=('Start Cluster'), show='tree')
logical_tree.pack(fill=tk.BOTH, expand=True)
# Far right frame (logical file details)
far_right_frame = tk.Frame(splitter_frame, width=frame_width)
splitter_frame.add(far_right_frame, stretch="always")
# Logical file list tree view
logical_file_tree = ttk.Treeview(far_right_frame, columns=('Name', 'Size', 'Start Cluster', 'Deleted'), show='headings')
logical_file_tree.heading('Name', text='Name')
logical_file_tree.heading('Size', text='Size')
logical_file_tree.heading('Start Cluster', text='Start Cluster')
logical_file_tree.heading('Deleted', text='Deleted')
logical_file_tree.pack(fill=tk.BOTH, expand=True)
# Logical file details and data frames
logical_details_frame = tk.Frame(far_right_frame, height=300)
logical_details_frame.pack(fill=tk.BOTH, expand=True)
logical_details_text = tk.Text(logical_details_frame, height=10)
logical_details_text.pack(fill=tk.BOTH, expand=True)
logical_data_text = tk.Text(logical_details_frame, height=10)
logical_data_text.pack(fill=tk.BOTH, expand=True)
# Bottom frame for physical and logical drive info
bottom_frame = tk.Frame(root)
bottom_frame.pack(side=tk.BOTTOM, fill=tk.X)
# Left side of bottom frame (for physical drive info)
physical_drive_info_frame = tk.Frame(bottom_frame, width=550)
physical_drive_info_frame.pack(side=tk.LEFT, fill=tk.X)
selected_drive_label = tk.Label(physical_drive_info_frame, text="선택한 드라이브: None")
selected_drive_label.pack(side=tk.LEFT, padx=5, pady=5)
# Right side of bottom frame (for logical drive file operations)
logical_button_frame = tk.Frame(bottom_frame, width=550)
logical_button_frame.pack(side=tk.RIGHT, fill=tk.X)
# 논리 드라이브 폴더 및 파일 생성 버튼 추가
add_logical_folder_btn = tk.Button(logical_button_frame, text="폴더 추가(논리)", command=lambda: create_logical_folder(logical_tree, logical_drive_var.get()))
add_logical_folder_btn.pack(side=tk.LEFT, padx=5, pady=5)
add_logical_file_btn = tk.Button(logical_button_frame, text="파일 추가(논리)", command=lambda: create_logical_file(logical_tree, logical_drive_var.get()))
add_logical_file_btn.pack(side=tk.LEFT, padx=5, pady=5)
delete_logical_file_btn = tk.Button(logical_button_frame, text="파일 삭제", command=lambda: delete_logical_file(logical_tree, logical_drive_var.get()))
delete_logical_file_btn.pack(side=tk.LEFT, padx=5, pady=5)
edit_logical_file_btn = tk.Button(logical_button_frame, text="수정", command=lambda: edit_logical_file(logical_tree, logical_drive_var.get()))
edit_logical_file_btn.pack(side=tk.LEFT, padx=5, pady=5)
def on_select():
global selected_drive, root_dir_sector, sectors_per_cluster, start_lba, reserved_sectors, fat_size
selected_drive_info = drive_var.get()
selected_drive = selected_drive_info.split(' - ')[0]
print(f"선택한 드라이브: {selected_drive}")
try:
mbr = read_mbr(selected_drive)
partitions = parse_partitions(mbr)
print(f"MBR 기반 파티션: {partitions}")
except Exception as e:
print(f"MBR 읽기 실패 또는 오류: {e}")
partitions = []
if partitions:
# MBR 기반 FAT32 파티션 있음
start_lba = partitions[0]['start_lba']
root_dir, root_dir_sector, bytes_per_sector, sectors_per_cluster, reserved_sectors, fat_size = read_fat32_root(selected_drive, start_lba)
print(f"MBR 기반 FAT32 감지됨. 시작 섹터: {start_lba}")
else:
# MBR 없이 FAT32 부트 섹터가 섹터 0에 존재하는지 확인
boot_sector = read_sector(selected_drive, 0)
fs_type = boot_sector[82:90]
signature = boot_sector[510:512]
if fs_type == b'FAT32 ' and signature == b'\x55\xAA':
print("MBR 없이 FAT32 부트 섹터 감지됨.")
start_lba = 0
root_dir, root_dir_sector, bytes_per_sector, sectors_per_cluster, reserved_sectors, fat_size = read_fat32_root(selected_drive, start_lba)
else:
messagebox.showerror("Error", "FAT32 파티션 또는 부트 섹터를 찾을 수 없습니다.")
return
selected_drive_label.config(text=f"선택한 물리 디스크: {selected_drive_info}")
entries = parse_directory_entries(root_dir)
for i in physical_tree.get_children():
physical_tree.delete(i)
root_node = physical_tree.insert('', 'end', text="Root Directory", values=(2,), open=True)
display_directory_structure(physical_tree, root_node, entries)
physical_tree.bind('<<TreeviewOpen>>', lambda event: on_physical_tree_open(event, physical_tree, file_tree, selected_drive, root_dir_sector, details_text, data_text, start_lba, reserved_sectors, fat_size))
physical_tree.bind('<<TreeviewSelect>>', lambda event: on_folder_select(event, physical_tree, file_tree, selected_drive, root_dir_sector, details_text, data_text, start_lba, reserved_sectors, fat_size))
logical_tree.bind('<<TreeviewOpen>>', lambda event: on_logical_tree_open(event, logical_tree))
logical_tree.bind('<<TreeviewSelect>>', lambda event: on_logical_folder_select(event, logical_tree, logical_file_tree, logical_details_text, logical_data_text))
root.mainloop()
if __name__ == "__main__":
main()