Skip to content

Commit e9d09e1

Browse files
committed
feat: Add image resize, usage UI & save-wipe logic
Introduce image resizing and improve usage reporting/UI across the app. Key changes: - LibLpSharp: add MetadataBuilder.ResizeBlockDevice with validation for existing extents; minor formatting/comment cleanup. MetadataWriter removes redundant comments and pads geometry as before. - PartitionToolSharp.Desktop: add fields to MetadataChangedMessage (UsagePercentage, UsageText, ImageSize) to propagate usage and image size to UI. - PartitionEntry: simplify FileSystemSizeText, add unit-aware DisplaySize/UI selection (Units, SelectedSizeUnit), notify DisplaySize on size changes. - DashboardViewModel: consume new message fields, format device/usage strings consistently, move GroupInfo type to bottom of file. - PartitionManagerViewModel: add ImageSize handling, UI-friendly DisplayImageSize and units, track removed extents (_extentsToWipe) for wiping on save, provide GetUpdatedBuilder helper, ResizeImageCommand to apply image resize via builder, update save flow to physically resize file, wipe data for deleted extents, clear extents list, and call RefreshProbeAsync; send richer MetadataChangedMessage when metadata updates; minor refactors and status messages. - Views (.axaml): overhaul Dashboard, Flasher and PartitionManager layouts/templates (cards, borders, typed DataTemplates, new controls for image resize, Refresh/Resize buttons, data-binding adjustments) to present usage, recent files, and image/partition editing more clearly. - Project: change TrimMode from "link" to "partial" in Desktop csproj. Overall this commit implements image resizing end-to-end (UI, model builder, save-time file resize and wiping), improves usage reporting, and refreshes several UI components for clearer display and unit editing.
1 parent 34e3ac2 commit e9d09e1

10 files changed

Lines changed: 374 additions & 147 deletions

LibLpSharp/Builder.cs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,9 @@ private void Init(ulong deviceSize, uint metadataMaxSize, uint metadataSlotCount
7575
StructSize = (uint)System.Runtime.InteropServices.Marshal.SizeOf<LpMetadataGeometry>(),
7676
MetadataMaxSize = metadataMaxSize,
7777
MetadataSlotCount = metadataSlotCount,
78-
LogicalBlockSize = 4096 // 默认为 4KB
78+
LogicalBlockSize = 4096
7979
};
8080

81-
// 添加默认块设备
8281
var super = new LpMetadataBlockDevice
8382
{
8483
Alignment = 4096,
@@ -151,6 +150,37 @@ public void AddGroup(string name, ulong maxSize)
151150

152151
public Partition? FindPartition(string name) => _partitions.FirstOrDefault(p => p.Name == name);
153152

153+
public void ResizeBlockDevice(ulong newSize)
154+
{
155+
if (_blockDevices.Count == 0)
156+
{
157+
return;
158+
}
159+
160+
// 验证新大小是否足够容纳现有的所有 extent
161+
var maxSectorUsed = _blockDevices[0].FirstLogicalSector;
162+
foreach (var p in _partitions)
163+
{
164+
foreach (var extent in p.Extents)
165+
{
166+
if (extent.TargetType == MetadataFormat.LP_TARGET_TYPE_LINEAR)
167+
{
168+
maxSectorUsed = Math.Max(maxSectorUsed, extent.TargetData + extent.NumSectors);
169+
}
170+
}
171+
}
172+
173+
var minRequiredSize = maxSectorUsed * MetadataFormat.LP_SECTOR_SIZE;
174+
if (newSize < minRequiredSize)
175+
{
176+
throw new InvalidOperationException($"无法调整镜像大小:新容量 ({newSize / (1024 * 1024.0):F2} MiB) 小于分区占用的最小空间 ({minRequiredSize / (1024 * 1024.0):F2} MiB)");
177+
}
178+
179+
var device = _blockDevices[0];
180+
device.Size = newSize;
181+
_blockDevices[0] = device;
182+
}
183+
154184
public void ResizePartition(Partition partition, ulong requestedSize)
155185
{
156186
if (partition.Size == requestedSize)

LibLpSharp/MetadataWriter.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ public static unsafe byte[] SerializeGeometry(LpMetadataGeometry geometry)
99
geometry.Magic = MetadataFormat.LP_METADATA_GEOMETRY_MAGIC;
1010
geometry.StructSize = (uint)sizeof(LpMetadataGeometry);
1111

12-
// 计算校验和之前先清零
1312
for (var i = 0; i < 32; i++)
1413
{
1514
geometry.Checksum[i] = 0;
@@ -34,7 +33,6 @@ public static unsafe byte[] SerializeGeometry(LpMetadataGeometry geometry)
3433
}
3534
}
3635

37-
// 填充至 LP_METADATA_GEOMETRY_SIZE 大小
3836
var padded = new byte[MetadataFormat.LP_METADATA_GEOMETRY_SIZE];
3937
Array.Copy(blob, padded, blob.Length);
4038
return padded;

PartitionToolSharp.Desktop/Models/MetadataChangedMessage.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,7 @@ namespace PartitionToolSharp.Desktop.Models;
66
public class MetadataChangedMessage(LpMetadata? metadata, string? path = null) : ValueChangedMessage<LpMetadata?>(metadata)
77
{
88
public string? Path { get; } = path;
9+
public double? UsagePercentage { get; set; }
10+
public string? UsageText { get; set; }
11+
public ulong? ImageSize { get; set; }
912
}

PartitionToolSharp.Desktop/Models/PartitionEntry.cs

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -56,26 +56,44 @@ public bool IsSlot
5656
[NotifyPropertyChangedFor(nameof(FileSystemSizeText))]
5757
private string _fileSystemType = "Unknown";
5858

59-
public string FileSystemSizeText
60-
{
61-
get
62-
{
63-
if (FileSystemType == "Unknown") return "Unformatted";
64-
if (FileSystemType == "Raw") return "No FS Header";
65-
66-
if (FileSystemSize > 0)
67-
{
68-
return $"({FileSystemSize / (1024 * 1024.0):F2} MiB)";
69-
}
70-
return string.Empty;
71-
}
72-
}
59+
public string FileSystemSizeText => FileSystemType == "Unknown"
60+
? "Unformatted"
61+
: FileSystemType == "Raw" ? "No FS Header" : FileSystemSize > 0 ? $"({FileSystemSize / (1024 * 1024.0):F2} MiB)" : string.Empty;
7362

7463
public Action? OnChanged { get; set; }
7564

76-
partial void OnSizeChanged(ulong value) => OnChanged?.Invoke();
65+
partial void OnSizeChanged(ulong value)
66+
{
67+
OnChanged?.Invoke();
68+
OnPropertyChanged(nameof(DisplaySize));
69+
}
7770
partial void OnNameChanged(string value) => OnChanged?.Invoke();
7871
partial void OnAttributesChanged(uint value) => OnChanged?.Invoke();
7972

8073
public string SizeInMiB => $"{Size / (1024 * 1024.0):F2} MiB";
74+
75+
public static readonly string[] Units = ["B", "KB", "MB", "GB"];
76+
77+
[ObservableProperty]
78+
private string _selectedSizeUnit = "MB";
79+
80+
public double DisplaySize
81+
{
82+
get => Size / GetUnitFactor(SelectedSizeUnit);
83+
set
84+
{
85+
Size = (ulong)(value * GetUnitFactor(SelectedSizeUnit));
86+
OnPropertyChanged(nameof(DisplaySize));
87+
}
88+
}
89+
90+
partial void OnSelectedSizeUnitChanged(string value) => OnPropertyChanged(nameof(DisplaySize));
91+
92+
private static double GetUnitFactor(string unit) => unit switch
93+
{
94+
"KB" => 1024.0,
95+
"MB" => 1024.0 * 1024.0,
96+
"GB" => 1024.0 * 1024.0 * 1024.0,
97+
_ => 1.0
98+
};
8199
}

PartitionToolSharp.Desktop/PartitionToolSharp.Desktop.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
<!-- Native AOT Support -->
1212
<PublishAot>true</PublishAot>
13-
<TrimMode>link</TrimMode>
13+
<TrimMode>partial</TrimMode>
1414
<IsTrimmable>true</IsTrimmable>
1515
<StackTraceSupport>false</StackTraceSupport>
1616
</PropertyGroup>

PartitionToolSharp.Desktop/ViewModels/DashboardViewModel.cs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,21 @@ public void Receive(MetadataChangedMessage message)
6262
ConfigService.Save();
6363
}
6464
UpdateStats(message.Value);
65+
66+
if (message.UsagePercentage.HasValue)
67+
{
68+
UsagePercentage = message.UsagePercentage.Value;
69+
}
70+
if (message.UsageText != null)
71+
{
72+
UsageText = message.UsageText;
73+
}
74+
75+
if (message.ImageSize.HasValue)
76+
{
77+
var totalSizeGb = message.ImageSize.Value / (1024 * 1024 * 1024.0);
78+
DeviceSize = $"{totalSizeGb:F2} GB";
79+
}
6580
}
6681

6782
[RelayCommand]
@@ -70,13 +85,6 @@ public void Receive(MetadataChangedMessage message)
7085
[RelayCommand]
7186
private void OpenRecentFile(string path) => WeakReferenceMessenger.Default.Send(new OpenImageRequestMessage(path));
7287

73-
public class GroupInfo
74-
{
75-
public string Name { get; set; } = "";
76-
public string SizeText { get; set; } = "";
77-
public string FlagsText { get; set; } = "";
78-
}
79-
8088
public void UpdateStats(LpMetadata? metadata)
8189
{
8290
if (metadata == null)
@@ -96,11 +104,11 @@ public void UpdateStats(LpMetadata? metadata)
96104
}
97105

98106
var totalSize = metadata.BlockDevices[0].Size;
99-
DeviceSize = $"{totalSize / (1024 * 1024 * 1024.0):F2} GB";
107+
var totalSizeGb = totalSize / (1024 * 1024 * 1024.0);
108+
DeviceSize = $"{totalSizeGb:F2} GB";
100109
SlotCount = metadata.Geometry.MetadataSlotCount.ToString();
101110
MetadataSize = $"{metadata.Geometry.MetadataMaxSize / 1024.0:F0} KB";
102111

103-
// Calculate usage: sum of all extents used by partitions
104112
ulong usedSectors = 0;
105113
foreach (var extent in metadata.Extents)
106114
{
@@ -111,7 +119,15 @@ public void UpdateStats(LpMetadata? metadata)
111119
}
112120

113121
var usedBytes = usedSectors * 512;
122+
var usedBytesGb = usedBytes / (1024 * 1024 * 1024.0);
114123
UsagePercentage = (double)usedBytes / totalSize * 100;
115-
UsageText = $"{UsagePercentage:F1}% Used ({usedBytes / (1024 * 1024 * 1024.0):F2} GB / {DeviceSize})";
124+
UsageText = $"{UsagePercentage:F1}% Used ({usedBytesGb:F2} GB / {DeviceSize})";
116125
}
117126
}
127+
128+
public class GroupInfo
129+
{
130+
public string Name { get; set; } = "";
131+
public string SizeText { get; set; } = "";
132+
public string FlagsText { get; set; } = "";
133+
}

0 commit comments

Comments
 (0)