diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e625743..e425d80 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,6 +39,26 @@ jobs: if: matrix.build-type == 'framework-dependent' run: dotnet publish GDeflateGUI/GDeflateGUI.csproj --configuration Release --output ./publish-gui --no-build --self-contained false + - name: Download and copy dependencies + run: | + # Download nvCOMP library for CUDA 12 + $nvcompUrl = "https://developer.nvidia.com/downloads/compute/nvcompdx/redist/nvcompdx/cuda12/nvidia-mathdx-25.06.1-cuda12.zip" + $nvcompZip = "nvcomp.zip" + Invoke-WebRequest -Uri $nvcompUrl -OutFile $nvcompZip + + # Extract the contents + Expand-Archive -Path $nvcompZip -DestinationPath ./nvcomp_temp + + # Copy required DLLs to the publish directory + # Adjust the source path based on the actual structure of the zip file + Copy-Item -Path ./nvcomp_temp/bin/nvcomp.dll -Destination ./publish-gui/ + Copy-Item -Path ./nvcomp_temp/bin/cudart64_12.dll -Destination ./publish-gui/ + + # Clean up downloaded files + Remove-Item $nvcompZip + Remove-Item -Recurse -Force ./nvcomp_temp + shell: pwsh + - name: Create build info run: | echo "GDeflate GUI Application - ${{ matrix.build-type }}" > ./publish-gui/BUILD_INFO.txt @@ -94,6 +114,27 @@ jobs: - name: Publish console application (Self-contained) run: dotnet publish GDeflateConsole/GDeflateConsole.csproj --configuration Release --output ./publish-console --self-contained true --runtime ${{ matrix.runtime }} + - name: Download and copy dependencies (Windows only) + if: matrix.os == 'windows-latest' + run: | + # Download nvCOMP library for CUDA 12 + $nvcompUrl = "https://developer.nvidia.com/downloads/compute/nvcompdx/redist/nvcompdx/cuda12/nvidia-mathdx-25.06.1-cuda12.zip" + $nvcompZip = "nvcomp.zip" + Invoke-WebRequest -Uri $nvcompUrl -OutFile $nvcompZip + + # Extract the contents + Expand-Archive -Path $nvcompZip -DestinationPath ./nvcomp_temp + + # Copy required DLLs to the publish directory + # Adjust the source path based on the actual structure of the zip file + Copy-Item -Path ./nvcomp_temp/bin/nvcomp.dll -Destination ./publish-console/ + Copy-Item -Path ./nvcomp_temp/bin/cudart64_12.dll -Destination ./publish-console/ + + # Clean up downloaded files + Remove-Item $nvcompZip + Remove-Item -Recurse -Force ./nvcomp_temp + shell: pwsh + - name: Create build info (Windows) if: matrix.os == 'windows-latest' run: | diff --git a/GDeflateConsole/Program.cs b/GDeflateConsole/Program.cs index 2eba7a1..7625250 100644 --- a/GDeflateConsole/Program.cs +++ b/GDeflateConsole/Program.cs @@ -330,29 +330,14 @@ static void TestCompressionDecompression(string testFile, GDeflateProcessor proc Console.WriteLine($" Decompressed size: {FormatFileSize(decompressedInfo.Length)}"); Console.WriteLine($" Decompression time: {decompressTime.TotalMilliseconds:F2} ms"); - // Verify integrity (for simulation mode) - if (processor.IsSimulationMode) + // Verify integrity + Console.WriteLine(" Verifying file integrity..."); + bool filesMatch = AreFilesEqual(testFile, decompressedFile); + Console.WriteLine($" Integrity check: {(filesMatch ? "PASSED" : "FAILED")}"); + + if (!filesMatch) { - Console.WriteLine($" Integrity check: Simulation mode (basic verification)"); - } - else - { - // For real GPU mode, we could do byte-by-byte comparison - bool identical = originalInfo.Length == decompressedInfo.Length; - if (identical && originalInfo.Length < 10 * 1024 * 1024) // Only for files < 10MB - { - var originalBytes = File.ReadAllBytes(testFile); - var decompressedBytes = File.ReadAllBytes(decompressedFile); - - for (int i = 0; i < originalBytes.Length && identical; i++) - { - if (originalBytes[i] != decompressedBytes[i]) - { - identical = false; - } - } - } - Console.WriteLine($" Integrity check: {(identical ? "PASSED" : "FAILED")}"); + throw new Exception("File integrity check failed: decompressed file does not match original."); } Console.WriteLine(" Test PASSED"); @@ -372,6 +357,44 @@ static void TestCompressionDecompression(string testFile, GDeflateProcessor proc } } + static bool AreFilesEqual(string path1, string path2) + { + const int bufferSize = 4096; + + using (var fs1 = new FileStream(path1, FileMode.Open, FileAccess.Read)) + using (var fs2 = new FileStream(path2, FileMode.Open, FileAccess.Read)) + { + if (fs1.Length != fs2.Length) + { + return false; + } + + var buffer1 = new byte[bufferSize]; + var buffer2 = new byte[bufferSize]; + + while (true) + { + int bytesRead1 = fs1.Read(buffer1, 0, bufferSize); + int bytesRead2 = fs2.Read(buffer2, 0, bufferSize); + + if (bytesRead1 != bytesRead2) + { + return false; + } + + if (bytesRead1 == 0) + { + return true; + } + + if (!buffer1.AsSpan(0, bytesRead1).SequenceEqual(buffer2.AsSpan(0, bytesRead2))) + { + return false; + } + } + } + } + static string FormatFileSize(long bytes) { string[] suffixes = { "B", "KB", "MB", "GB", "TB" }; diff --git a/GDeflateGUI/MainForm.cs b/GDeflateGUI/MainForm.cs index 568470b..bdce5b5 100644 --- a/GDeflateGUI/MainForm.cs +++ b/GDeflateGUI/MainForm.cs @@ -138,72 +138,87 @@ await System.Threading.Tasks.Task.Run(() => private void btnAddFiles_Click(object? sender, System.EventArgs e) { + if (!IsWindows) + { + AddFilesAlternativeMethod(); + return; + } + try { - if (IsWindows) + using (var dialog = new OpenFileDialog { - // Use Windows Forms dialog on Windows - using (var dialog = new OpenFileDialog()) + Multiselect = true, + Title = "Select files", + Filter = "All files (*.*)|*.*" + }) + { + if (dialog.ShowDialog() == DialogResult.OK) { - dialog.Multiselect = true; - dialog.Title = "Select files"; - dialog.Filter = "All files (*.*)|*.*"; - if (dialog.ShowDialog() == DialogResult.OK) - { - foreach (string file in dialog.FileNames) - { - AddFileToList(file); - } - UpdateStatus($"Added {dialog.FileNames.Length} files. Total: {listViewFiles.Items.Count}"); - } + AddFilesToList(dialog.FileNames); } } - else - { - // Alternative method for non-Windows platforms - AddFilesAlternativeMethod(); - } } catch (Exception ex) { - MessageBox.Show($"Error adding files: {ex.Message}\n\nTip: On non-Windows platforms, you can manually enter file paths or use the alternative method.", - "File Selection Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); - UpdateStatus("Error occurred while adding files. See message for details."); + ShowError("Error adding files", ex); } } - private void btnAddFolder_Click(object? sender, System.EventArgs e) + private async void btnAddFolder_Click(object? sender, System.EventArgs e) { + if (!IsWindows) + { + AddFolderAlternativeMethod(); + return; + } + try { - if (IsWindows) + using (var dialog = new FolderBrowserDialog + { + Description = "Select a folder to add all its files" + }) { - // Use Windows Forms dialog on Windows - using (var dialog = new FolderBrowserDialog()) + if (dialog.ShowDialog() == DialogResult.OK) { - dialog.Description = "Select a folder to add all its files"; - if (dialog.ShowDialog() == DialogResult.OK) - { - var files = Directory.GetFiles(dialog.SelectedPath, "*.*", SearchOption.AllDirectories); - foreach (string file in files) - { - AddFileToList(file); - } - UpdateStatus($"Added {files.Length} files from folder. Total: {listViewFiles.Items.Count}"); - } + await AddFolderFilesAsync(dialog.SelectedPath); } } - else - { - // Alternative method for non-Windows platforms - AddFolderAlternativeMethod(); - } } catch (Exception ex) { - MessageBox.Show($"Error adding folder: {ex.Message}\n\nTip: On non-Windows platforms, you can manually enter folder paths or use the alternative method.", - "Folder Selection Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); - UpdateStatus("Error occurred while adding folder. See message for details."); + ShowError("Error adding folder", ex); + } + } + + private async System.Threading.Tasks.Task AddFolderFilesAsync(string path) + { + SetUIEnabled(false); + UpdateStatus("Searching for files..."); + + try + { + var files = await System.Threading.Tasks.Task.Run(() => + Directory.GetFiles(path, "*.*", SearchOption.AllDirectories) + ); + AddFilesToList(files); + } + catch (UnauthorizedAccessException ex) + { + ShowError("Access denied. You may not have permission to access all subdirectories.", ex); + } + catch (IOException ex) + { + ShowError("An I/O error occurred while accessing the folder.", ex); + } + catch (Exception ex) + { + ShowError("An unexpected error occurred while adding folder files.", ex); + } + finally + { + SetUIEnabled(true); } } @@ -237,16 +252,11 @@ private void AddFilesAlternativeMethod() .Take(5) // Limit to first 5 files .ToArray(); - foreach (string file in files) - { - AddFileToList(file); - } - - UpdateStatus($"Added {files.Length} test files from current directory. Total: {listViewFiles.Items.Count}"); + AddFilesToList(files); } catch (Exception ex) { - MessageBox.Show($"Error adding test files: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + ShowError("Error adding test files", ex); } } } @@ -271,31 +281,36 @@ private void AddFolderAlternativeMethod() { var currentDir = Directory.GetCurrentDirectory(); var files = Directory.GetFiles(currentDir, "*.*", SearchOption.AllDirectories); - - foreach (string file in files) - { - AddFileToList(file); - } - - UpdateStatus($"Added {files.Length} files from current directory and subdirectories. Total: {listViewFiles.Items.Count}"); + AddFilesToList(files); } catch (Exception ex) { - MessageBox.Show($"Error adding files from folder: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + ShowError("Error adding files from folder", ex); } } } - private void AddFileToList(string filePath) + private void AddFilesToList(string[] filePaths) { - // Check if the file is already in the list to avoid duplicates - if (!listViewFiles.Items.Cast().Any(item => item.Text == filePath)) + var newItems = filePaths + .Where(filePath => !listViewFiles.Items.Cast().Any(item => item.Text == filePath)) + .Select(filePath => new ListViewItem(filePath)) + .ToArray(); + + if (newItems.Any()) { - var item = new ListViewItem(filePath); - listViewFiles.Items.Add(item); + listViewFiles.Items.AddRange(newItems); + UpdateStatus($"Added {newItems.Length} files. Total: {listViewFiles.Items.Count}"); } } + private void ShowError(string title, Exception ex) + { + MessageBox.Show($"{ex.Message}\n\nTip: On non-Windows platforms, you can use the console version for more detailed error information.", + title, MessageBoxButtons.OK, MessageBoxIcon.Warning); + UpdateStatus($"Error: {title}. See message for details."); + } + private void UpdateStatus(string text) { statusLabel.Text = text;