Implement complete Windows USB writing functionality

- Replace placeholder with full Windows USB writing implementation
- Use diskpart for USB drive formatting and partitioning
- Implement file copying with robocopy and Python shutil fallback
- Add bootloader file copying to make USB bootable
- Enhance USB device detection on Windows:
  - Show drive letters, volume names, and sizes
  - Fallback to Windows API for drive type detection
  - Better user-friendly device labeling
- Resolves 'Windows USB writing not implemented' error
parent 81773817
...@@ -221,8 +221,106 @@ class WorkerThread(QThread): ...@@ -221,8 +221,106 @@ class WorkerThread(QThread):
os.unlink(temp_iso) os.unlink(temp_iso)
def write_usb_windows(self, source_dir): def write_usb_windows(self, source_dir):
# Windows implementation - could use Rufus API or similar # Windows USB writing implementation
raise Exception("Windows USB writing not implemented in this version. Please use Linux.") import tempfile
import subprocess
# Get USB drive letter (assuming it's like 'D:')
usb_drive = self.usb_device
if not usb_drive.endswith(':'):
usb_drive += ':'
self.status.emit(f"Preparing USB drive {usb_drive}...")
# Create diskpart script to format the USB drive
diskpart_script = f"""
select volume {usb_drive[0]}
clean
create partition primary
active
format fs=fat32 quick label="MBetter Live"
assign
exit
"""
# Write diskpart script to temporary file
with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as script_file:
script_file.write(diskpart_script)
script_path = script_file.name
try:
# Run diskpart to format the USB drive
self.status.emit("Formatting USB drive...")
result = subprocess.run([
'diskpart', '/s', script_path
], capture_output=True, text=True, check=True)
# Clean up diskpart script
os.unlink(script_path)
# Copy all files from source directory to USB drive
self.status.emit("Copying files to USB drive...")
self._copy_directory_windows(source_dir, f"{usb_drive}\\")
# Make it bootable by ensuring boot files are in the right place
self._make_bootable_windows(source_dir, usb_drive)
self.status.emit("USB creation completed successfully!")
except subprocess.CalledProcessError as e:
if script_path and os.path.exists(script_path):
os.unlink(script_path)
raise Exception(f"Failed to format USB drive: {e}")
except Exception as e:
if script_path and os.path.exists(script_path):
os.unlink(script_path)
raise Exception(f"USB creation failed: {e}")
def _copy_directory_windows(self, src_dir, dst_dir):
"""Copy directory contents using robocopy for better Windows compatibility"""
try:
# Use robocopy for efficient directory copying
subprocess.run([
'robocopy', src_dir, dst_dir, '/E', '/R:3', '/W:1'
], check=False) # robocopy returns non-zero even on success
except Exception:
# Fallback to Python shutil if robocopy fails
import shutil
for item in os.listdir(src_dir):
src_path = os.path.join(src_dir, item)
dst_path = os.path.join(dst_dir, item)
if os.path.isdir(src_path):
shutil.copytree(src_path, dst_path, dirs_exist_ok=True)
else:
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
shutil.copy2(src_path, dst_path)
def _make_bootable_windows(self, source_dir, usb_drive):
"""Make the USB drive bootable by copying necessary boot files"""
# Copy bootloader files to root if they exist
boot_files = [
'bootmgr',
'bootmgfw.efi',
'isolinux',
'boot',
'EFI'
]
for boot_file in boot_files:
src_path = os.path.join(source_dir, boot_file)
if os.path.exists(src_path):
dst_path = os.path.join(f"{usb_drive}\\", boot_file)
try:
if os.path.isdir(src_path):
if os.path.exists(dst_path):
import shutil
shutil.rmtree(dst_path)
shutil.copytree(src_path, dst_path)
else:
shutil.copy2(src_path, dst_path)
except Exception as e:
# Non-critical error, continue
self.status.emit(f"Warning: Could not copy {boot_file}: {e}")
class USBCreatorApp(QMainWindow): class USBCreatorApp(QMainWindow):
...@@ -424,21 +522,59 @@ class USBCreatorApp(QMainWindow): ...@@ -424,21 +522,59 @@ class USBCreatorApp(QMainWindow):
devices = [] devices = []
if platform.system() == "Windows": if platform.system() == "Windows":
# Windows implementation # Windows implementation - get removable drives
try: try:
result = subprocess.run(['wmic', 'logicaldisk', 'get', 'deviceid,description,size'], # Get removable drives using wmic
capture_output=True, text=True) result = subprocess.run([
lines = result.stdout.strip().split('\n')[1:] 'wmic', 'logicaldisk', 'where', 'drivetype=2',
'get', 'deviceid,volumename,size', '/format:csv'
], capture_output=True, text=True)
lines = result.stdout.strip().split('\n')[1:] # Skip header
for line in lines: for line in lines:
if 'Removable' in line: if line.strip() and ',' in line:
parts = line.split() parts = line.split(',')
if len(parts) >= 2: if len(parts) >= 4 and parts[1]: # Has drive letter
drive_letter = parts[1].strip()
volume_name = parts[3].strip() if len(parts) > 3 else ""
size = parts[2].strip() if len(parts) > 2 else ""
# Convert size to readable format
try:
if size:
size_gb = int(size) / (1024**3)
size_str = f"{size_gb:.1f} GB"
else:
size_str = "Unknown size"
except:
size_str = "Unknown size"
label = f"{drive_letter} - {volume_name} ({size_str})" if volume_name else f"{drive_letter} - Removable Drive ({size_str})"
devices.append({ devices.append({
'path': parts[0], 'path': drive_letter,
'label': f"{parts[0]} - Removable Drive" 'label': label
}) })
except: except Exception as e:
pass # Fallback method
try:
import string
for drive_letter in string.ascii_uppercase:
drive_path = f"{drive_letter}:\\"
if os.path.exists(drive_path):
# Check if it's removable using GetDriveType
try:
import ctypes
drive_type = ctypes.windll.kernel32.GetDriveTypeW(drive_path)
if drive_type == 2: # DRIVE_REMOVABLE
devices.append({
'path': f"{drive_letter}:",
'label': f"{drive_letter}: - Removable Drive"
})
except:
pass
except:
pass
else: else:
# Linux implementation # Linux implementation
try: try:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment