Skip to content

Commit 6304307

Browse files
committed
FileForge: Redesign merge compose
1 parent 81a6426 commit 6304307

8 files changed

Lines changed: 677 additions & 123 deletions

File tree

FileForge/FileForge/Core/FileEngine.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,26 @@ public static void MergeFiles(IList<string> inputPaths, string outputPath, byte[
456456
}
457457
}
458458

459+
/// <summary>
460+
/// Compose an output file from an arbitrary ordered sequence of file and fill-buffer segments.
461+
/// </summary>
462+
public static void MergeSegments(IList<MergeSegmentData> segments, string outputPath)
463+
{
464+
if (segments == null || segments.Count == 0)
465+
throw new ArgumentException("At least one segment is required.");
466+
467+
using (var output = new FileStream(outputPath, FileMode.Create, FileAccess.Write))
468+
{
469+
foreach (var seg in segments)
470+
{
471+
if (seg.IsFile)
472+
CopyFileToStream(seg.FilePath, output);
473+
else
474+
WriteFillData(output, seg.FillMode, seg.FillSize, seg.SpecificByte, seg.HexPattern);
475+
}
476+
}
477+
}
478+
459479
// ── Region operations ──────────────────────────────────────────────────
460480

461481
public static void ExtractRegion(string inputPath, string outputPath, long offset, long size)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
namespace FileForge.Core
2+
{
3+
/// <summary>
4+
/// Describes a single segment in a composed merge operation:
5+
/// either a source file or a generated fill buffer.
6+
/// </summary>
7+
public class MergeSegmentData
8+
{
9+
/// <summary>true = file segment; false = fill-buffer segment.</summary>
10+
public bool IsFile { get; set; }
11+
12+
// ── File segment ──────────────────────────────────────────────────────
13+
14+
public string FilePath { get; set; }
15+
16+
// ── Buffer segment ────────────────────────────────────────────────────
17+
18+
public long FillSize { get; set; }
19+
public FillMode FillMode { get; set; }
20+
public byte SpecificByte { get; set; }
21+
public byte[] HexPattern { get; set; }
22+
}
23+
}

FileForge/FileForge/FileForge.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,13 @@
128128
<Compile Include="Core\FileEngine.cs" />
129129
<Compile Include="Core\HashEngine.cs" />
130130
<Compile Include="Core\HashResult.cs" />
131+
<Compile Include="Core\MergeSegmentData.cs" />
131132
<Compile Include="Core\PatchEntry.cs" />
132133
</ItemGroup>
133134
<!-- View helpers -->
134135
<ItemGroup>
135136
<Compile Include="Views\DiffRow.cs" />
137+
<Compile Include="Views\MergeSegment.cs" />
136138
<Compile Include="Views\ViewHelper.cs" />
137139
</ItemGroup>
138140
<!-- View XAML pages -->
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
using System.ComponentModel;
2+
using System.IO;
3+
using FileForge.Core;
4+
5+
namespace FileForge.Views
6+
{
7+
public enum SegmentKind { File, Buffer }
8+
9+
/// <summary>
10+
/// UI-level model for one segment in the merge composition sequence.
11+
/// Implements INotifyPropertyChanged so the ListView DataTemplate updates live.
12+
/// </summary>
13+
public class MergeSegment : INotifyPropertyChanged
14+
{
15+
public event PropertyChangedEventHandler PropertyChanged;
16+
private void Notify(string name) =>
17+
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
18+
19+
// ── Kind ──────────────────────────────────────────────────────────────
20+
21+
private SegmentKind _kind;
22+
public SegmentKind Kind
23+
{
24+
get => _kind;
25+
set { _kind = value; Notify(nameof(Kind)); Notify(nameof(TypeLabel)); Notify(nameof(Summary)); }
26+
}
27+
28+
// ── File fields ───────────────────────────────────────────────────────
29+
30+
private string _filePath = "";
31+
public string FilePath
32+
{
33+
get => _filePath;
34+
set { _filePath = value ?? ""; Notify(nameof(FilePath)); Notify(nameof(Summary)); }
35+
}
36+
37+
// ── Buffer fields ──────────────────────────────────────────────────────
38+
39+
private string _fillSizeText = "512";
40+
public string FillSizeText
41+
{
42+
get => _fillSizeText;
43+
set { _fillSizeText = value ?? "512"; Notify(nameof(FillSizeText)); Notify(nameof(Summary)); }
44+
}
45+
46+
private string _fillSizeUnit = "Bytes";
47+
public string FillSizeUnit
48+
{
49+
get => _fillSizeUnit;
50+
set { _fillSizeUnit = value ?? "Bytes"; Notify(nameof(FillSizeUnit)); Notify(nameof(Summary)); }
51+
}
52+
53+
private int _fillModeIndex;
54+
public int FillModeIndex
55+
{
56+
get => _fillModeIndex;
57+
set { _fillModeIndex = value; Notify(nameof(FillModeIndex)); Notify(nameof(Summary)); }
58+
}
59+
60+
private string _specificByte = "FF";
61+
public string SpecificByte
62+
{
63+
get => _specificByte;
64+
set { _specificByte = value ?? "FF"; Notify(nameof(SpecificByte)); Notify(nameof(Summary)); }
65+
}
66+
67+
private string _hexPattern = "DE AD BE EF";
68+
public string HexPattern
69+
{
70+
get => _hexPattern;
71+
set { _hexPattern = value ?? ""; Notify(nameof(HexPattern)); Notify(nameof(Summary)); }
72+
}
73+
74+
// ── Computed display properties ───────────────────────────────────────
75+
76+
public string TypeLabel => Kind == SegmentKind.File ? "FILE" : "BUFFER";
77+
78+
public string Summary
79+
{
80+
get
81+
{
82+
if (Kind == SegmentKind.File)
83+
{
84+
if (string.IsNullOrWhiteSpace(_filePath)) return "(no file selected)";
85+
string name = Path.GetFileName(_filePath);
86+
try
87+
{
88+
if (File.Exists(_filePath))
89+
return name + " (" + FileEngine.FormatSize(new FileInfo(_filePath).Length) + ")";
90+
}
91+
catch { }
92+
return name;
93+
}
94+
else
95+
{
96+
string fillPart;
97+
switch (_fillModeIndex)
98+
{
99+
case 1: fillPart = "Byte 0x" + _specificByte.Trim().ToUpperInvariant(); break;
100+
case 2: fillPart = "Hex Pattern"; break;
101+
case 3: fillPart = "Random"; break;
102+
default: fillPart = "Zeros"; break;
103+
}
104+
return _fillSizeText + " " + _fillSizeUnit + " \u2014 " + fillPart;
105+
}
106+
}
107+
}
108+
109+
/// <summary>Parses FillSizeText + FillSizeUnit into bytes. Returns 0 if invalid.</summary>
110+
public long FillSizeBytes
111+
{
112+
get
113+
{
114+
try { return FileEngine.ParseSize(_fillSizeText, _fillSizeUnit); }
115+
catch { return 0; }
116+
}
117+
}
118+
}
119+
}

0 commit comments

Comments
 (0)