From e0b74eb522dd86533b8a037fe6d6e4e6025a0c3e Mon Sep 17 00:00:00 2001 From: Elektro <13082613+ElektroStudios@users.noreply.github.com> Date: Sat, 14 Sep 2024 11:11:46 +0200 Subject: [PATCH] v1.3 --- Docs/CHANGELOG.md | 11 +- README.md | 1 + .../DevCase/LogfileUtil.vb | 85 ------- .../DevCase/LogfileWriter.vb | 230 ----------------- .../DevCase/Win32/NativeMethods_ShlwApi.vb | 71 ++++++ .../DevCase/Win32/StrFormatByteSizeFlags.vb | 30 +++ .../DevCase/Win32/Win32LibNames.vb | 5 + .../Form1.Designer.vb | 18 +- Source/PS3 Quick Disc Decryptor/Form1.resx | 27 ++ Source/PS3 Quick Disc Decryptor/Form1.vb | 232 ++++++++++++++++-- .../PS3 Quick Disc Decryptor.vbproj | 5 +- .../ProgramSettings.vb | 6 +- Source/PS3 Quick Disc Decryptor/README.md | 1 + .../RestoreDefaultValuesEditor.vb | 2 +- 14 files changed, 368 insertions(+), 356 deletions(-) delete mode 100644 Source/PS3 Quick Disc Decryptor/DevCase/LogfileUtil.vb delete mode 100644 Source/PS3 Quick Disc Decryptor/DevCase/LogfileWriter.vb create mode 100644 Source/PS3 Quick Disc Decryptor/DevCase/Win32/NativeMethods_ShlwApi.vb create mode 100644 Source/PS3 Quick Disc Decryptor/DevCase/Win32/StrFormatByteSizeFlags.vb diff --git a/Docs/CHANGELOG.md b/Docs/CHANGELOG.md index 0fd9577..3b4382e 100644 --- a/Docs/CHANGELOG.md +++ b/Docs/CHANGELOG.md @@ -1,11 +1,18 @@ # PS3 Quick Disc Decryptor Change Log πŸ“‹ -## v1.2 *(current)* πŸ†• +## v1.3 *(current)* πŸ†• +#### 🌟 New Features: + β€’ Added support for zip archives (for both PS3 disc images and decryption key files). +#### 🌟 Improvements: + β€’ Additional PS3Dec process error information when compact mode is enabled. + β€’ Implemented checks for the required disk space needed for unzipping and decrypting + +## v1.2 πŸ”„ #### 🌟 Improvements: β€’ Added basic PS3 disc image file (*.iso) validation. β€’ Added progress on Windows task bar. #### πŸ› οΈ Fixes: - β€’ The status label disappear when the string exceeds the window bounds. + β€’ The status label disappear when the text exceeds the control bounds. ## v1.1 πŸ”„ #### πŸš€ New Features: diff --git a/README.md b/README.md index b27c695..c56b9aa 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ The decrypted PS3 disc images will work with [RPCS3](https://rpcs3.net/) emulato - Simple, user-friendly graphical user-interface. - Designed for batch processing. + - Supports zip archives (for both PS3 disc images and decryption key files). - Meticulous status report and error handling. - Logging features. - Allows to abort the decryption procedure on demand. diff --git a/Source/PS3 Quick Disc Decryptor/DevCase/LogfileUtil.vb b/Source/PS3 Quick Disc Decryptor/DevCase/LogfileUtil.vb deleted file mode 100644 index 35af873..0000000 --- a/Source/PS3 Quick Disc Decryptor/DevCase/LogfileUtil.vb +++ /dev/null @@ -1,85 +0,0 @@ -ο»Ώ'Option Strict On -'Option Explicit On -'Option Infer Off - -'Imports System.ComponentModel -'Imports System.IO -'Imports System.Text - -'Namespace DevCase.Core.Diagnostics.Logging - -' Public Class LogfileUtil - -' Public Property EntryFormat As String = "[{0}] [{1}] | {2,-11} | {3}" - - -' Public Sub WriteEntry(eventType As TraceEventType, -' message As String) - -' Dim localDate As String = Date.Now.Date.ToShortDateString() -' Dim localTime As String = Date.Now.ToLongTimeString() -' Me.sw.WriteLine(String.Format(Me.EntryFormat, localDate, localTime, eventType.ToString(), message)) -' End Sub - -' -' Public Sub WriteText(text As String) -' Me.sw.WriteLine(text) -' End Sub - -' Public Sub WriteNewLine() -' Me.sw.WriteLine() -' End Sub - -' -' Public Sub Clear() - -' Me.sw.Close() - -' Dim bufferSize As Integer = 1024 ' StreamWriterDefault = BufferSizes.Kb1 - -' Me.sw = New StreamWriter(Me.Filepath, append:=False, encoding:=Me.Encoding, bufferSize:=bufferSize) With {.AutoFlush = True} -' Me.sw.BaseStream.SetLength(0) -' End Sub - -'#End Region - -'#Region " IDisposable Implementation " - -' ''' -' ''' Flag to detect redundant calls when disposing. -' ''' -' Private isDisposed As Boolean = False - -' ''' -' ''' Releases all the resources used by this instance. -' ''' -' -' Public Sub Dispose() Implements IDisposable.Dispose -' Me.Dispose(isDisposing:=True) -' GC.SuppressFinalize(Me) -' End Sub - -' ''' -' ''' Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. -' ''' Releases unmanaged and, optionally, managed resources. -' ''' -' ''' -' ''' -' ''' to release both managed and unmanaged resources; -' ''' to release only unmanaged resources. -' ''' -' -' Protected Sub Dispose(isDisposing As Boolean) - -' If (Not Me.isDisposed) AndAlso isDisposing Then -' Me.sw?.Close() -' End If - -' Me.isDisposed = True -' End Sub - -'#End Region - -' End Class - -'End Namespace diff --git a/Source/PS3 Quick Disc Decryptor/DevCase/LogfileWriter.vb b/Source/PS3 Quick Disc Decryptor/DevCase/LogfileWriter.vb deleted file mode 100644 index 61c5e20..0000000 --- a/Source/PS3 Quick Disc Decryptor/DevCase/LogfileWriter.vb +++ /dev/null @@ -1,230 +0,0 @@ -ο»Ώ'Option Strict On -'Option Explicit On -'Option Infer Off - -'Imports System.ComponentModel -'Imports System.IO -'Imports System.Text - -'Namespace DevCase.Core.Diagnostics.Logging - -' ''' -' ''' A simple logging system assistant that helps to create a log for the current application. -' ''' -' ''' -' ''' This is a code example. -' ''' -' ''' Public Class Form1 : Inherits Form -' ''' -' ''' Private logfile As New LogfileWriter(String.Format("{0}.log", My.Application.Info.AssemblyName)) With -' ''' { -' ''' .EntryFormat = "[{1}] | {2,-11} | {3}" -' ''' } ' {0}=Date, {1}=Time, {2}=Event, {3}=Message. -' ''' -' ''' Private Sub Form1_Load() Handles MyBase.Load -' ''' -' ''' With Me.logfile -' ''' .Clear() -' ''' .WriteText("#########################################") -' ''' .WriteNewLine() -' ''' .WriteText(String.Format(" Log Date {0} ", Date.Now.Date.ToShortDateString)) -' ''' .WriteNewLine() -' ''' .WriteText("#########################################") -' ''' .WriteNewLine() -' ''' .WriteEntry(TraceEventType.Information, "Application is being initialized.") -' ''' End With -' ''' -' ''' Try -' ''' Dim setting As Integer = Integer.Parse(" Hello World! :D ") -' ''' -' ''' Catch ex As Exception -' ''' Me.logfile.WriteEntry(TraceEventType.Critical, "Cannot parse 'setting' object in 'Sub Form1_Load()' method.") -' ''' Me.logfile.WriteEntry(TraceEventType.Information, "Exiting...") -' ''' Application.Exit() -' ''' -' ''' End Try -' ''' -' ''' End Sub -' ''' -' ''' End Class -' ''' -' ''' -' -' Public Class LogfileWriter : Implements IDisposable - -'#Region " Private Fields " - -' ''' -' ''' The where is written the logging data -' ''' -' Private sw As StreamWriter - -'#End Region - -'#Region " Properties " - -' ''' -' ''' Gets the log filepath. -' ''' -' ''' -' ''' -' ''' The log filepath. -' ''' -' Public ReadOnly Property Filepath As String - -' ''' -' ''' Gets the logfile encoding. -' ''' -' ''' -' ''' -' ''' The logfile encoding. -' ''' -' Public ReadOnly Property Encoding As Encoding - -' ''' -' ''' Gets or sets the format of a log entry. -' ''' {0}=Date, {1}=Time, {2}=Event, {3}=Message. -' ''' -' ''' -' ''' -' ''' The format of a log entry. -' ''' -' Public Property EntryFormat As String = "[{0}] [{1}] | {2,-11} | {3}" - -'#End Region - -'#Region " Constructors " - -' ''' -' ''' Prevents a default instance of the class from being created. -' ''' -' -' Private Sub New() -' End Sub - -' ''' -' ''' Initializes a new instance of the class. -' ''' -' ''' -' ''' -' ''' The log filepath. -' ''' -' ''' -' ''' -' ''' The file encoding. -' ''' -' -' Public Sub New(filepath As String, Optional enc As Encoding = Nothing) - -' If enc Is Nothing Then -' enc = Encoding.Default -' End If - -' Me.Filepath = filepath -' Me.Encoding = enc -' Dim bufferSize As Integer = 1024 ' StreamWriterDefault = BufferSizes.Kb1 -' Me.sw = New StreamWriter(filepath, append:=True, encoding:=enc, bufferSize:=bufferSize) With {.AutoFlush = True} -' End Sub - -'#End Region - -'#Region " Public Methods " - -' ''' -' ''' Writes a new entry on the logfile. -' ''' -' ''' -' ''' -' ''' The type of event. -' ''' -' ''' -' ''' -' ''' The message to log. -' ''' -' -' Public Sub WriteEntry(eventType As TraceEventType, -' message As String) - -' Dim localDate As String = Date.Now.Date.ToShortDateString -' Dim localTime As String = Date.Now.ToLongTimeString - -' Me.sw.WriteLine(String.Format(Me.EntryFormat, localDate, localTime, eventType.ToString(), message)) -' End Sub - -' ''' -' ''' Writes any text on the logfile. -' ''' -' ''' -' ''' -' ''' The text to write. -' ''' -' -' Public Sub WriteText(text As String) - -' Me.sw.WriteLine(text) -' End Sub - -' ''' -' ''' Writes an empty line on the logfile. -' ''' -' -' Public Sub WriteNewLine() - -' Me.sw.WriteLine() -' End Sub - -' ''' -' ''' Clears the logfile content. -' ''' -' -' Public Sub Clear() -' Me.sw.Close() - -' Dim bufferSize As Integer = 1024 ' StreamWriterDefault = BufferSizes.Kb1 - -' Me.sw = New StreamWriter(Me.Filepath, append:=False, encoding:=Me.Encoding, bufferSize:=bufferSize) With {.AutoFlush = True} -' Me.sw.BaseStream.SetLength(0) -' End Sub - -'#End Region - -'#Region " IDisposable Implementation " - -' ''' -' ''' Flag to detect redundant calls when disposing. -' ''' -' Private isDisposed As Boolean = False - -' ''' -' ''' Releases all the resources used by this instance. -' ''' -' -' Public Sub Dispose() Implements IDisposable.Dispose -' Me.Dispose(isDisposing:=True) -' GC.SuppressFinalize(Me) -' End Sub - -' ''' -' ''' Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. -' ''' Releases unmanaged and, optionally, managed resources. -' ''' -' ''' -' ''' -' ''' to release both managed and unmanaged resources; -' ''' to release only unmanaged resources. -' ''' -' -' Protected Sub Dispose(isDisposing As Boolean) - -' If (Not Me.isDisposed) AndAlso isDisposing Then -' Me.sw?.Close() -' End If - -' Me.isDisposed = True -' End Sub - -'#End Region - -' End Class - -'End Namespace diff --git a/Source/PS3 Quick Disc Decryptor/DevCase/Win32/NativeMethods_ShlwApi.vb b/Source/PS3 Quick Disc Decryptor/DevCase/Win32/NativeMethods_ShlwApi.vb new file mode 100644 index 0000000..0088828 --- /dev/null +++ b/Source/PS3 Quick Disc Decryptor/DevCase/Win32/NativeMethods_ShlwApi.vb @@ -0,0 +1,71 @@ +ο»ΏOption Strict On +Option Explicit On +Option Infer Off + +Imports System.Runtime.InteropServices +Imports System.Runtime.Versioning +Imports System.Security +Imports System.Text + +Imports DevCase.Win32.Enums + +Imports MS.WindowsAPICodePack.Internal + +Namespace DevCase.Win32.NativeMethods + + ''' + ''' Platform Invocation methods (P/Invoke), access unmanaged code. + ''' + ''' ShlwApi.dll. + ''' + + + + Friend Module ShlwApi + +#Region " ShlwApi.dll " + + ''' + ''' Converts a numeric value into a string that represents the number expressed as a size value in bytes, + ''' kilobytes, megabytes, gigabytes, petabytes or exabytes, depending on the size. + ''' + ''' Extends StrFormatByteSizeW by offering the option to round to the nearest displayed digit or to discard undisplayed digits. + ''' + ''' + ''' + ''' + ''' + ''' + ''' + ''' The numeric value to be converted. + ''' + ''' + ''' + ''' Specifies whether to round or truncate undisplayed digits. + ''' + ''' + ''' + ''' A pointer to a buffer that, when this function returns successfully, receives the converted number. + ''' + ''' + ''' + ''' The size of , in characters. + ''' + ''' + ''' + ''' If this function succeeds, it returns "HResult.S_OK". + ''' Otherwise, it returns an "HResult" error code. + ''' + + Public Function StrFormatByteSizeEx(number As ULong, + flags As StrFormatByteSizeFlags, + buffer As StringBuilder, + bufferSize As UInteger + ) As Integer ' HResult + End Function + +#End Region + + End Module + +End Namespace diff --git a/Source/PS3 Quick Disc Decryptor/DevCase/Win32/StrFormatByteSizeFlags.vb b/Source/PS3 Quick Disc Decryptor/DevCase/Win32/StrFormatByteSizeFlags.vb new file mode 100644 index 0000000..23cf23b --- /dev/null +++ b/Source/PS3 Quick Disc Decryptor/DevCase/Win32/StrFormatByteSizeFlags.vb @@ -0,0 +1,30 @@ +ο»Ώ +Option Strict On +Option Explicit On +Option Infer Off + +Namespace DevCase.Win32.Enums + + ''' + ''' Specifies whether to round or truncate undisplayed digits when calling + ''' function. + ''' + ''' + ''' + ''' + ''' + Public Enum StrFormatByteSizeFlags + + ''' + ''' Round to the nearest displayed digit. + ''' + RoundToNearest = 1 + + ''' + ''' Discard undisplayed digits. + ''' + Truncate = 2 + + End Enum + +End Namespace diff --git a/Source/PS3 Quick Disc Decryptor/DevCase/Win32/Win32LibNames.vb b/Source/PS3 Quick Disc Decryptor/DevCase/Win32/Win32LibNames.vb index 10320b1..1674e98 100644 --- a/Source/PS3 Quick Disc Decryptor/DevCase/Win32/Win32LibNames.vb +++ b/Source/PS3 Quick Disc Decryptor/DevCase/Win32/Win32LibNames.vb @@ -13,6 +13,11 @@ Namespace DevCase.Win32 Friend Module Win32LibNames + ''' + ''' ShlwApi.dll + ''' + Friend Const ShlwApi As String = "ShlwApi.dll" + ''' ''' User32.dll ''' diff --git a/Source/PS3 Quick Disc Decryptor/Form1.Designer.vb b/Source/PS3 Quick Disc Decryptor/Form1.Designer.vb index 5732177..a46e7c5 100644 --- a/Source/PS3 Quick Disc Decryptor/Form1.Designer.vb +++ b/Source/PS3 Quick Disc Decryptor/Form1.Designer.vb @@ -45,7 +45,7 @@ Partial Class Form1 Me.PropertyGrid_Settings.Name = "PropertyGrid_Settings" Me.PropertyGrid_Settings.PropertySort = PropertySort.Categorized Me.PropertyGrid_Settings.Size = New Size(642, 266) - Me.PropertyGrid_Settings.TabIndex = 9 + Me.PropertyGrid_Settings.TabIndex = 1 Me.PropertyGrid_Settings.ToolbarVisible = False ' ' Button_StartDecryption @@ -54,7 +54,7 @@ Partial Class Form1 Me.Button_StartDecryption.Location = New Point(3, 6) Me.Button_StartDecryption.Name = "Button_StartDecryption" Me.Button_StartDecryption.Size = New Size(127, 33) - Me.Button_StartDecryption.TabIndex = 10 + Me.Button_StartDecryption.TabIndex = 0 Me.Button_StartDecryption.Text = "Start Decryption" Me.Button_StartDecryption.UseVisualStyleBackColor = True ' @@ -64,7 +64,7 @@ Partial Class Form1 Me.ProgressBar_Decryption.Location = New Point(270, 6) Me.ProgressBar_Decryption.Name = "ProgressBar_Decryption" Me.ProgressBar_Decryption.Size = New Size(369, 33) - Me.ProgressBar_Decryption.TabIndex = 12 + Me.ProgressBar_Decryption.TabIndex = 2 ' ' StatusStrip1 ' @@ -73,7 +73,7 @@ Partial Class Form1 Me.StatusStrip1.Name = "StatusStrip1" Me.StatusStrip1.Padding = New Padding(2, 0, 13, 0) Me.StatusStrip1.Size = New Size(648, 22) - Me.StatusStrip1.TabIndex = 13 + Me.StatusStrip1.TabIndex = 0 Me.StatusStrip1.Text = "StatusStrip1" ' ' ToolStripStatusLabel1 @@ -81,7 +81,7 @@ Partial Class Form1 Me.ToolStripStatusLabel1.DisplayStyle = ToolStripItemDisplayStyle.Text Me.ToolStripStatusLabel1.Font = New Font("Segoe UI", 9F, FontStyle.Regular, GraphicsUnit.Point) Me.ToolStripStatusLabel1.Name = "ToolStripStatusLabel1" - Me.ToolStripStatusLabel1.Size = New Size(602, 17) + Me.ToolStripStatusLabel1.Size = New Size(633, 17) Me.ToolStripStatusLabel1.Spring = True Me.ToolStripStatusLabel1.Text = "..." Me.ToolStripStatusLabel1.TextAlign = ContentAlignment.MiddleLeft @@ -97,7 +97,7 @@ Partial Class Form1 Me.Button_Abort.Location = New Point(136, 6) Me.Button_Abort.Name = "Button_Abort" Me.Button_Abort.Size = New Size(128, 33) - Me.Button_Abort.TabIndex = 14 + Me.Button_Abort.TabIndex = 1 Me.Button_Abort.Text = "Abort" Me.Button_Abort.UseVisualStyleBackColor = True ' @@ -113,7 +113,7 @@ Partial Class Form1 Me.TextBox_PS3Dec_Output.Name = "TextBox_PS3Dec_Output" Me.TextBox_PS3Dec_Output.ReadOnly = True Me.TextBox_PS3Dec_Output.Size = New Size(642, 144) - Me.TextBox_PS3Dec_Output.TabIndex = 0 + Me.TextBox_PS3Dec_Output.TabIndex = 2 ' ' TableLayoutPanel1 ' @@ -130,7 +130,7 @@ Partial Class Form1 Me.TableLayoutPanel1.RowStyles.Add(New RowStyle(SizeType.Percent, 32F)) Me.TableLayoutPanel1.RowStyles.Add(New RowStyle(SizeType.Percent, 10F)) Me.TableLayoutPanel1.Size = New Size(648, 470) - Me.TableLayoutPanel1.TabIndex = 15 + Me.TableLayoutPanel1.TabIndex = 1 ' ' Panel_Buttons ' @@ -141,7 +141,7 @@ Partial Class Form1 Me.Panel_Buttons.Location = New Point(3, 425) Me.Panel_Buttons.Name = "Panel_Buttons" Me.Panel_Buttons.Size = New Size(642, 42) - Me.Panel_Buttons.TabIndex = 16 + Me.Panel_Buttons.TabIndex = 0 ' ' Form1 ' diff --git a/Source/PS3 Quick Disc Decryptor/Form1.resx b/Source/PS3 Quick Disc Decryptor/Form1.resx index 2ff7855..4447404 100644 --- a/Source/PS3 Quick Disc Decryptor/Form1.resx +++ b/Source/PS3 Quick Disc Decryptor/Form1.resx @@ -117,12 +117,39 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + True + + + True + + + True + 17, 17 + + True + 133, 17 + + True + + + True + + + True + + + True + + + True + diff --git a/Source/PS3 Quick Disc Decryptor/Form1.vb b/Source/PS3 Quick Disc Decryptor/Form1.vb index b3fcf85..01dc44e 100644 --- a/Source/PS3 Quick Disc Decryptor/Form1.vb +++ b/Source/PS3 Quick Disc Decryptor/Form1.vb @@ -6,11 +6,14 @@ Imports System.ComponentModel Imports System.IO Imports System.IO.Compression Imports System.Runtime.InteropServices +Imports System.Runtime.Versioning Imports System.Text Imports System.Threading +Imports System.Windows.Media.Animation Imports DevCase.Extensions Imports DevCase.Win32 +Imports DevCase.Win32.Enums Imports DiscUtils @@ -18,6 +21,8 @@ Imports DiscUtils.Iso9660 Imports Microsoft.WindowsAPICodePack.Taskbar +Imports MS.WindowsAPICodePack.Internal + Friend NotInheritable Class Form1 #Region " Private Fields " @@ -30,6 +35,11 @@ Friend NotInheritable Class Form1 Private keys As IEnumerable(Of FileInfo) Private isoAndKeyPairs As IDictionary(Of FileInfo, FileInfo) + ''' + ''' Directory where to unzip zipped isos and decryption keys. + ''' + Private ReadOnly tempFolderPath As String = Path.Combine(Path.GetTempPath, My.Application.Info.Title) + ''' ''' CMD process to embed its window in the UI, and where PS3Dec.exe will run. ''' @@ -83,7 +93,6 @@ Friend NotInheritable Class Form1 Private Sub Form1_Shown(sender As Object, e As EventArgs) _ Handles MyBase.Shown - Me.MinimumSize = Me.Size Me.LoadUserSettings() Me.Opacity = 100 @@ -117,7 +126,7 @@ Friend NotInheritable Class Form1 If Not e.Cancel Then Me.SaveUserSettings() - Me.UpdateStatus("Program is being closed.", writeToLogFile:=True) + Me.UpdateStatus("Program is being closed...", writeToLogFile:=True) Me.DeinitializeLogger() End If End Sub @@ -199,6 +208,8 @@ Friend NotInheritable Class Form1 Me.UpdateStatus($"Starting a new decryption procedure...", writeToLogFile:=True) Me.ResetProgressBar() + Me.ClearTempFiles("*") + If Not Me.FetchISOs() OrElse Not Me.FetchDecryptionKeys() OrElse Not Me.BuildisoAndKeyPairs() OrElse @@ -222,6 +233,7 @@ Friend NotInheritable Class Form1 If Not Me.BackgroundWorker1.CancellationPending Then Me.ProcessDecryption(pair, currentIsoIndex, totalIsoCount) + Me.ClearTempFiles(Path.GetFileNameWithoutExtension(pair.Key.Name)) Me.UpdateProgressBar(totalIsoCount, Interlocked.Increment(currentIsoIndex)) End If Next @@ -274,22 +286,25 @@ Friend NotInheritable Class Form1 #Region " Private Methods " ''' - ''' Fetches the encrypted PS3 *.iso files. + ''' Fetches the encrypted PS3 *.iso / *.zip files. ''' ''' if successful, otherwise. Private Function FetchISOs() As Boolean - Me.UpdateStatus("Fetching encrypted PS3 ISOs...", writeToLogFile:=True) + Me.UpdateStatus("Fetching encrypted PS3 disc images...", writeToLogFile:=True) Try - Me.isos = Form1.Settings.EncryptedPS3DiscsDir?.GetFiles("*.iso", SearchOption.TopDirectoryOnly) + Me.isos = Form1.Settings.EncryptedPS3DiscsDir?. + GetFiles("*.*", SearchOption.TopDirectoryOnly). + Where(Function(x) x.Extension.ToLowerInvariant() = ".iso" OrElse + x.Extension.ToLowerInvariant() = ".zip") Catch ex As Exception - Form1.ShowMessageBoxInUIThread(Me, "Error fetching encrypted PS3 ISOs", ex.Message, MessageBoxIcon.Error) + Form1.ShowMessageBoxInUIThread(Me, "Error fetching encrypted PS3 disc images", ex.Message, MessageBoxIcon.Error) Return False End Try If Not Me.isos.Any() Then - Form1.ShowMessageBoxInUIThread(Me, "Error fetching encrypted PS3 ISOs", $"Can't find any ISO file in the specified directory: '{Form1.Settings.EncryptedPS3DiscsDir}'", MessageBoxIcon.Error) + Form1.ShowMessageBoxInUIThread(Me, "Error fetching encrypted PS3 disc images", $"Can't find any ISO or ZIP file in the specified directory: '{Form1.Settings.EncryptedPS3DiscsDir}'", MessageBoxIcon.Error) Return False End If @@ -307,7 +322,8 @@ Friend NotInheritable Class Form1 Me.keys = Form1.Settings.DecryptionKeysDir?. GetFiles("*.*", SearchOption.TopDirectoryOnly). Where(Function(x) x.Extension.ToLowerInvariant() = ".dkey" OrElse - x.Extension.ToLowerInvariant() = ".txt") + x.Extension.ToLowerInvariant() = ".txt" OrElse + x.Extension.ToLowerInvariant() = ".zip") Catch ex As Exception Form1.ShowMessageBoxInUIThread(Me, "Error fetching decryption keys", ex.Message, MessageBoxIcon.Error) Return False @@ -363,10 +379,16 @@ Friend NotInheritable Class Form1 ''' Validates a PS3 ISO file. ''' ''' if successful, otherwise. - Private Function ValidatePS3Iso(iso As FileInfo) As Boolean + Private Function ValidatePS3Iso(ByRef refIso As FileInfo, percentage As Integer, currentIsoIndex As Integer, totalIsoCount As Integer) As Boolean + refIso = Me.UnzipFileIfNecessary(refIso, percentage, currentIsoIndex, totalIsoCount, "PS3 ISO") + If refIso Is Nothing Then + Return False + End If + + Me.UpdateStatus($"{percentage}% ({currentIsoIndex}/{totalIsoCount}) | {refIso.Name} | Validating encrypted PS3 ISO...", writeToLogFile:=True) Try - Using isoStream As FileStream = File.OpenRead(iso.FullName), + Using isoStream As FileStream = File.OpenRead(refIso.FullName), cd As New CDReader(isoStream, joliet:=False) Dim isExpectedClusterSize As Boolean = cd.ClusterSize = 2048 @@ -378,13 +400,13 @@ Friend NotInheritable Class Form1 If Not isExpectedClusterSize OrElse Not isExpectedVolumeLabel OrElse Not existsPS3GAMEDir Then - Form1.ShowMessageBoxInUIThread(Me, "Error validating encrypted PS3 ISO", $"The ISO file is not a PS3 disc image: {iso.FullName}", MessageBoxIcon.Error) + Form1.ShowMessageBoxInUIThread(Me, "Error validating encrypted PS3 ISO", $"The ISO file is not a PS3 disc image: {refIso.FullName}", MessageBoxIcon.Error) Return False End If End Using Catch ex As Exception - Form1.ShowMessageBoxInUIThread(Me, $"Error validating encrypted PS3 PS3 ISO.{Environment.NewLine & Environment.NewLine}File: {iso.FullName}{Environment.NewLine & Environment.NewLine}Error message: {ex.Message}", ex.Message, MessageBoxIcon.Error) + Form1.ShowMessageBoxInUIThread(Me, $"Error validating encrypted PS3 PS3 ISO.{Environment.NewLine & Environment.NewLine}File: {refIso.FullName}{Environment.NewLine & Environment.NewLine}Error message: {ex.Message}", ex.Message, MessageBoxIcon.Error) Return False End Try @@ -420,22 +442,31 @@ Friend NotInheritable Class Form1 Dim percentage As Integer = CInt(currentIsoIndex / totalIsoCount * 100) - Me.UpdateStatus($"{percentage}% ({currentIsoIndex}/{totalIsoCount}) | {pair.Key.Name} | Parsing decryption key file content...", writeToLogFile:=True) Dim dkeyString As String = Nothing - If Not Me.ReadDecryptionKey(pair.Value, dkeyString) Then + If Not Me.ReadDecryptionKey(pair.Value, dkeyString, percentage, currentIsoIndex, totalIsoCount) Then Exit Sub End If - Me.UpdateStatus($"{percentage}% ({currentIsoIndex}/{totalIsoCount}) | {pair.Key.Name} | Validating encrypted PS3 ISO...", writeToLogFile:=True) - If Not Me.ValidatePS3Iso(pair.Key) Then + Dim refIso As FileInfo = pair.Key + Dim sizeString As String = Me.FormatFileSize(refIso.Length, StrFormatByteSizeFlags.RoundToNearest) + + If Not Me.ValidatePS3Iso(refIso, percentage, currentIsoIndex, totalIsoCount) Then Exit Sub End If - Me.UpdateStatus($"{percentage}% ({currentIsoIndex}/{totalIsoCount}) | {pair.Key.Name} | Writing decrypted PS3 ISO to output directory...", writeToLogFile:=True) + Me.UpdateStatus($"{percentage}% ({currentIsoIndex}/{totalIsoCount}) | {refIso.Name} | Writing decrypted PS3 ISO ({sizeString}) to output directory...", writeToLogFile:=True) If Not Me.EnsureOutputDirectoryExists() Then Exit Sub End If - Me.ExecutePS3Dec(pair.Key, dkeyString, currentIsoIndex, totalIsoCount) + + Dim drive As DriveInfo = Form1.Settings.DecryptionKeysDir.GetDriveInfo() + Dim requiredSizeString As String = Me.FormatFileSize(refIso.Length, StrFormatByteSizeFlags.RoundToNearest) + If Not Me.DriveHasFreeSpace(drive, refIso.Length) Then + Form1.ShowMessageBoxInUIThread(Me, $"Error writing decrypted PS3 ISO.", $"Drive {drive} requires {requiredSizeString} of free space to write the file.", MessageBoxIcon.Error) + Exit Sub + End If + + Me.ExecutePS3Dec(pair, refIso, dkeyString, currentIsoIndex, totalIsoCount) End Sub @@ -443,8 +474,14 @@ Friend NotInheritable Class Form1 ''' Reads and validates the decryption key from a file. ''' ''' if successful, otherwise. - Private Function ReadDecryptionKey(keyFile As FileInfo, ByRef refKeyString As String) As Boolean + Private Function ReadDecryptionKey(keyFile As FileInfo, ByRef refKeyString As String, percentage As Integer, currentIsoIndex As Integer, totalIsoCount As Integer) As Boolean + keyFile = Me.UnzipFileIfNecessary(keyFile, percentage, currentIsoIndex, totalIsoCount, "Decryption key") + If keyFile Is Nothing Then + Return False + End If + + Me.UpdateStatus($"{percentage}% ({currentIsoIndex}/{totalIsoCount}) | {keyFile.Name} | Parsing decryption key file content...", writeToLogFile:=True) Try Dim dkeyString As String = File.ReadAllText(keyFile.FullName, Encoding.Default).Trim() If dkeyString.Length <> 32 Then @@ -479,6 +516,7 @@ Friend NotInheritable Class Form1 ''' if successful, otherwise. Private Function EnsureOutputDirectoryExists() As Boolean + Form1.Settings.OutputDir.Refresh() If Not Form1.Settings.OutputDir.Exists Then Try Form1.Settings.OutputDir.Create() @@ -490,7 +528,7 @@ Friend NotInheritable Class Form1 Return True End Function - Private Sub ExecutePS3Dec(isoFile As FileInfo, dkeyString As String, currentIsoIndex As Integer, totalIsoCount As Integer) + Private Sub ExecutePS3Dec(pair As KeyValuePair(Of FileInfo, FileInfo), isoFile As FileInfo, dkeyString As String, currentIsoIndex As Integer, totalIsoCount As Integer) Dim currentProcess As Process @@ -564,6 +602,7 @@ Friend NotInheritable Class Form1 .StartInfo.FileName = Form1.Settings.PS3DecExeFile.FullName .StartInfo.Arguments = $"d key ""{dkeyString}"" ""{isoFile.FullName}"" ""{Form1.Settings.OutputDir.FullName}\{isoFile.Name}""" .StartInfo.CreateNoWindow = True + .StartInfo.RedirectStandardError = True End With Try @@ -578,10 +617,15 @@ Friend NotInheritable Class Form1 If currentProcess.ExitCode = 0 Then Dim percentage As Integer = CInt(currentIsoIndex / totalIsoCount * 100) Me.UpdateStatus($"{percentage}% ({currentIsoIndex}/{totalIsoCount}) | {isoFile.Name} | Decryption completed.", writeToLogFile:=True) - Me.CanDeleteEncryptedISO(isoFile) - Me.CanDeleteDecryptionKey(Me.isoAndKeyPairs(isoFile)) + Me.CanDeleteEncryptedISO(pair.Key) + Me.CanDeleteDecryptionKey(pair.Value) Else - Me.UpdateStatus($"PS3Dec.exe failed to decrypt, with exit code: {currentProcess.ExitCode}", writeToLogFile:=True) + If currentProcess.StartInfo.RedirectStandardError Then + Dim errorString As String = currentProcess.StandardError.ReadToEnd() + Me.UpdateStatus($"PS3Dec.exe failed to decrypt, with process exit code: {currentProcess.ExitCode} and error message: {errorString}", writeToLogFile:=True) + Else + Me.UpdateStatus($"PS3Dec.exe failed to decrypt, with process exit code: {currentProcess.ExitCode}", writeToLogFile:=True) + End If End If End Sub @@ -609,6 +653,106 @@ Friend NotInheritable Class Form1 End If End Sub + Private Sub ClearTempFiles(fileNamePattern As String) + Dim dir As New DirectoryInfo(Me.tempFolderPath) + If Not dir.Exists Then + Return + End If + + Me.UpdateStatus("Clearing temp files...", writeToLogFile:=False) + For Each file As FileInfo In dir.GetFiles($"{fileNamePattern}.*", SearchOption.TopDirectoryOnly) + Try + file.Delete() + Catch ex As Exception + ' Ignore. + End Try + Next file + End Sub + + ''' + ''' If the source file is a zip archive, unzips it to the temporary folder (). + ''' + ''' + ''' If the source file is a zip archive, returns a pointing to the unzipped file. + ''' Otherwise, returns the source . + ''' + Private Function UnzipFileIfNecessary(file As FileInfo, percentage As Integer, currentIsoIndex As Integer, totalIsoCount As Integer, additionalStatusInfoString As String) As FileInfo + + If Not file.Extension.Equals(".zip", StringComparison.OrdinalIgnoreCase) Then + Return file + End If + + Me.UpdateStatus($"{percentage}% ({currentIsoIndex}/{totalIsoCount}) | {file.Name} | Validating zip archive ({additionalStatusInfoString})...", writeToLogFile:=True) + If Not Me.ValidateZipArchive(file) Then + Return Nothing + End If + + Me.UpdateStatus($"{percentage}% ({currentIsoIndex}/{totalIsoCount}) | {file.Name} | Unzipping file ({additionalStatusInfoString})...", writeToLogFile:=True) + Try + Dim drive As DriveInfo = New DirectoryInfo(tempFolderPath).GetDriveInfo() + Const safetyExtraSize As Long = CLng(1024 ^ 2) * 300 ' 300 MB + Dim requiredSizeString As String = Me.FormatFileSize(file.Length + safetyExtraSize, StrFormatByteSizeFlags.RoundToNearest) + If Not Me.DriveHasFreeSpace(drive, file.Length) Then + Form1.ShowMessageBoxInUIThread(Me, $"Error extracting zip archive.", $"Drive {drive} requires {requiredSizeString} of free space to extract the zip archive.", MessageBoxIcon.Error) + Return Nothing + End If + + Using archive As ZipArchive = ZipFile.OpenRead(file.FullName) + + Dim entry As ZipArchiveEntry = archive.Entries.Single() + Dim destinationPath As String = Path.Combine(Me.tempFolderPath, entry.Name) + Dim unzippedFile As New FileInfo(destinationPath) + + If Not Directory.Exists(Me.tempFolderPath) Then + Directory.CreateDirectory(Me.tempFolderPath) + End If + + entry.ExtractToFile(unzippedFile.FullName, overwrite:=True) + unzippedFile.Refresh() + Return unzippedFile + End Using + + Catch ex As Exception + Form1.ShowMessageBoxInUIThread(Me, $"Error extracting zip archive.", ex.Message, MessageBoxIcon.Error) + + End Try + + Return Nothing + + End Function + + ''' + ''' Validates whether the specified file is a zip archive, + ''' and whether the zip archive only contains a single file. + ''' + ''' if successful, otherwise. + Private Function ValidateZipArchive(file As FileInfo) As Boolean + + Try + Using archive As ZipArchive = ZipFile.OpenRead(file.FullName) + Select Case archive.Entries.Count + Case 1 + Return True + Case 0 + Form1.ShowMessageBoxInUIThread(Me, $"Error validating zip archive.", $"The zip archive is empty: {file.FullName}", MessageBoxIcon.Error) + Return False + Case Else + Form1.ShowMessageBoxInUIThread(Me, $"Error validating zip archive.", $"The zip archive contains more than one file: {file.FullName}", MessageBoxIcon.Error) + Return False + End Select + End Using + + Catch ex As InvalidDataException + Return False + + Catch ex As Exception + Form1.ShowMessageBoxInUIThread(Me, $"Error validating zip archive.", ex.Message, MessageBoxIcon.Error) + Return False + + End Try + + End Function + Private Sub UpdateStatus(statusText As String, writeToLogFile As Boolean, Optional eventType As TraceEventType = TraceEventType.Information) Me.ToolStripStatusLabel1.Text = statusText If writeToLogFile Then @@ -685,7 +829,7 @@ Friend NotInheritable Class Form1 Private Sub SaveUserSettings() Try If Form1.Settings.SaveSettingsOnExit Then - Me.UpdateStatus($"Saving user settings...", writeToLogFile:=True) + Me.UpdateStatus($"Saving user settings...", writeToLogFile:=False) My.Settings.EncryptedPS3DiscsDir = Form1.Settings.EncryptedPS3DiscsDir.FullName My.Settings.DecryptionKeysDir = Form1.Settings.DecryptionKeysDir.FullName My.Settings.PS3DecExePath = Form1.Settings.PS3DecExeFile.FullName @@ -714,7 +858,7 @@ Friend NotInheritable Class Form1 Try If My.Settings.SaveSettingsOnExit Then - Me.UpdateStatus($"Loading user settings...", writeToLogFile:=True) + Me.UpdateStatus($"Loading user settings...", writeToLogFile:=False) Dim dirPath As String = My.Application.Info.DirectoryPath Form1.Settings.EncryptedPS3DiscsDir = New DirectoryInfo(My.Settings.EncryptedPS3DiscsDir.Replace(dirPath, ".")) Form1.Settings.DecryptionKeysDir = New DirectoryInfo(My.Settings.DecryptionKeysDir.Replace(dirPath, ".")) @@ -737,6 +881,44 @@ Friend NotInheritable Class Form1 End Try End Sub + + Public Function DriveHasFreeSpace(drive As DriveInfo, requiredSpaceInBytes As Long) As Boolean + + If drive.IsReady Then + Dim freeSpace As Long = drive.AvailableFreeSpace + Return freeSpace > requiredSpaceInBytes + Else + Return False + End If + + End Function + + Private Function FormatFileSize(bytes As Long, flags As StrFormatByteSizeFlags) As String + + Dim buffer As New StringBuilder(8, 16) + Dim result As Integer = NativeMethods.StrFormatByteSizeEx(CULng(bytes), flags, buffer, 16) + If result <> 0 Then ' HResult.S_OK + Marshal.ThrowExceptionForHR(result) + End If + + Return buffer.ToString() + + End Function + #End Region End Class + + +Module FileSystemInfoExtensions + + Public Function GetDriveInfo(fsi As IO.FileSystemInfo) As DriveInfo + + If fsi Is Nothing Then + Throw New ArgumentNullException(paramName:=NameOf(fsi)) + End If + + Dim driveName As String = Path.GetPathRoot(fsi.FullName) + Return New DriveInfo(driveName) + End Function +End Module \ No newline at end of file diff --git a/Source/PS3 Quick Disc Decryptor/PS3 Quick Disc Decryptor.vbproj b/Source/PS3 Quick Disc Decryptor/PS3 Quick Disc Decryptor.vbproj index 6979237..27545bf 100644 --- a/Source/PS3 Quick Disc Decryptor/PS3 Quick Disc Decryptor.vbproj +++ b/Source/PS3 Quick Disc Decryptor/PS3 Quick Disc Decryptor.vbproj @@ -4,6 +4,9 @@ WinExe net6.0-windows true + true + true + false false Sub Main @@ -17,7 +20,7 @@ Β© 2024 ElektroStudios icon.png en - 1.2 + 1.3 diff --git a/Source/PS3 Quick Disc Decryptor/ProgramSettings.vb b/Source/PS3 Quick Disc Decryptor/ProgramSettings.vb index b327cf1..4832424 100644 --- a/Source/PS3 Quick Disc Decryptor/ProgramSettings.vb +++ b/Source/PS3 Quick Disc Decryptor/ProgramSettings.vb @@ -11,8 +11,8 @@ Friend NotInheritable Class ProgramSettings - + Public Property EncryptedPS3DiscsDir As DirectoryInfo Private Function ShouldSerializeEncryptedPS3DiscsDir() As Boolean @@ -24,7 +24,7 @@ For more information, visit: http://redump.org/discs/system/ps3/")> +The files can be either *.dkey or *.txt containing a string of 32 characters long.")> Public Property DecryptionKeysDir As DirectoryInfo Private Function ShouldSerializeDecryptionKeysDir() As Boolean diff --git a/Source/PS3 Quick Disc Decryptor/README.md b/Source/PS3 Quick Disc Decryptor/README.md index e6c886c..d18c47d 100644 --- a/Source/PS3 Quick Disc Decryptor/README.md +++ b/Source/PS3 Quick Disc Decryptor/README.md @@ -16,6 +16,7 @@ The decrypted PS3 disc images will work with [RPCS3](https://rpcs3.net/) emulato - Simple, user-friendly graphical user-interface. - Designed for batch processing. + - Supports zip archives (for both PS3 disc images and decryption key files). - Meticulous status report and error handling. - Logging features. - Allows to abort the decryption procedure on demand. diff --git a/Source/PS3 Quick Disc Decryptor/UITypeEditors/RestoreDefaultValuesEditor.vb b/Source/PS3 Quick Disc Decryptor/UITypeEditors/RestoreDefaultValuesEditor.vb index c8b3db2..f9d4606 100644 --- a/Source/PS3 Quick Disc Decryptor/UITypeEditors/RestoreDefaultValuesEditor.vb +++ b/Source/PS3 Quick Disc Decryptor/UITypeEditors/RestoreDefaultValuesEditor.vb @@ -18,7 +18,7 @@ Friend NotInheritable Class RestoreDefaultValuesEditor : Inherits UITypeEditor Public Overrides Function EditValue(context As ITypeDescriptorContext, provider As IServiceProvider, value As Object) As Object Form1.Settings.SetDefaultValues() Form1.PropertyGrid_Settings.Refresh() - Form1.ShowMessageBoxInUIThread(Form1, My.Application.Info.Title, "Default values has been restored for program settings.", MessageBoxIcon.Information) + Form1.ShowMessageBoxInUIThread(Form1, My.Application.Info.Title, "The default values for the program settings have been restored.", MessageBoxIcon.Information) Return MyBase.EditValue(context, provider, value) End Function