1111using System . Linq ;
1212using System . Security . Cryptography ;
1313using System . Text ;
14+ using System . Xml . Linq ;
1415
1516namespace IRDKit
1617{
@@ -24,7 +25,7 @@ internal class Program
2425 [ Verb ( "create" , HelpText = "Create an IRD from an ISO" ) ]
2526 public class CreateOptions
2627 {
27- [ Value ( 0 , Required = true , HelpText = "Path to an ISO file, or directory of ISO files" ) ]
28+ [ Value ( 0 , Required = true , HelpText = "Path to ISO file(s) , or directory of ISO files" ) ]
2829 public IEnumerable < string > ISOPath { get ; set ; }
2930
3031 [ Option ( 'o' , "output" , HelpText = "Path to the IRD file to be created (will overwrite)" ) ]
@@ -55,7 +56,7 @@ public class CreateOptions
5556 [ Verb ( "info" , HelpText = "Print information from an IRD or ISO" ) ]
5657 public class InfoOptions
5758 {
58- [ Value ( 0 , Required = true , HelpText = "Path to an IRD or ISO file, or directory of IRD and/or ISO files" ) ]
59+ [ Value ( 0 , Required = true , HelpText = "Path to IRD/ ISO file(s) , or directory of IRD/ ISO files" ) ]
5960 public IEnumerable < string > InPath { get ; set ; }
6061
6162 [ Option ( 'o' , "output" , HelpText = "Path to the text or json file to be created (will overwrite)" ) ]
@@ -84,6 +85,31 @@ public class DiffOptions
8485 public string OutPath { get ; set ; }
8586 }
8687
88+ /// <summary>
89+ /// IRD rename command
90+ /// </summary>
91+ [ Verb ( "rename" , HelpText = "Rename one or more IRD files according to the redump PS3 DAT" ) ]
92+ public class RenameOptions
93+ {
94+ [ Value ( 0 , Required = true , HelpText = "Path to IRD file(s), or directory of IRD files" ) ]
95+ public IEnumerable < string > IRDPath { get ; set ; }
96+
97+ [ Option ( 'd' , "datfile" , Required = true , HelpText = "Path to the redump PS3 Datfile" ) ]
98+ public string DATPath { get ; set ; }
99+
100+ [ Option ( 's' , "serial" , HelpText = "Appends disc serial to new IRD filename" ) ]
101+ public bool Serial { get ; set ; }
102+
103+ [ Option ( 'c' , "crc" , HelpText = "Appends ISO CRC to new IRD filename" ) ]
104+ public bool CRC { get ; set ; }
105+
106+ [ Option ( 'r' , "recurse" , HelpText = "Recurse through all subdirectories and rename all IRDs" ) ]
107+ public bool Recurse { get ; set ; }
108+
109+ [ Option ( 'v' , "verbose" , HelpText = "Print more information about the renaming" ) ]
110+ public bool Verbose { get ; set ; }
111+ }
112+
87113 #endregion
88114
89115 #region Program
@@ -98,7 +124,7 @@ public static void Main(string[] args)
98124 Console . OutputEncoding = Encoding . UTF8 ;
99125
100126 // Parse arguments
101- var result = Parser . Default . ParseArguments < CreateOptions , InfoOptions , DiffOptions > ( args ) . WithParsed ( Run ) ;
127+ var result = Parser . Default . ParseArguments < CreateOptions , InfoOptions , DiffOptions , RenameOptions > ( args ) . WithParsed ( Run ) ;
102128 }
103129
104130 /// <summary>
@@ -173,7 +199,7 @@ private static void Run(object args)
173199 if ( ! File . Exists ( isoPath ) )
174200 {
175201 Console . Error . WriteLine ( $ "ISO not found: { isoPath } ") ;
176- return ;
202+ continue ;
177203 }
178204
179205 string irdPath ;
@@ -330,6 +356,99 @@ private static void Run(object args)
330356
331357 break ;
332358
359+ // Process options from a 'rename' command
360+ case RenameOptions opt :
361+
362+ // Validate required parameters
363+ if ( opt . IRDPath == null || ! opt . IRDPath . Any ( ) )
364+ {
365+ Console . Error . WriteLine ( "Provide a valid IRD path to rename" ) ;
366+ return ;
367+ }
368+
369+ // Read DAT file
370+ XDocument datfile = DatParser ( opt . DATPath ) ;
371+ if ( datfile == null )
372+ {
373+ Console . Error . WriteLine ( "Unable to parse DAT file" ) ;
374+ return ;
375+ }
376+
377+ foreach ( string irdPath in opt . IRDPath )
378+ {
379+ // Validate IRD path
380+ if ( string . IsNullOrEmpty ( irdPath ) )
381+ continue ;
382+
383+ // If directory, search for all ISOs in current directory
384+ if ( Directory . Exists ( irdPath ) )
385+ {
386+ // If recurse option enabled, search recursively
387+ IEnumerable < string > irdFiles ;
388+ if ( opt . Recurse )
389+ {
390+ if ( opt . Verbose && irdPath == "." )
391+ Console . WriteLine ( $ "Recursively renaming IRDs in current directory") ;
392+ else if ( opt . Verbose )
393+ Console . WriteLine ( $ "Recursively renaming IRDs in { irdPath } ") ;
394+ irdFiles = Directory . EnumerateFiles ( irdPath , "*.ird" , SearchOption . AllDirectories ) ;
395+ }
396+ else
397+ {
398+ if ( opt . Verbose && irdPath == "." )
399+ Console . WriteLine ( $ "Renaming IRDs in current directory") ;
400+ else if ( opt . Verbose )
401+ Console . WriteLine ( $ "Renaming IRDs in { irdPath } ") ;
402+ irdFiles = Directory . EnumerateFiles ( irdPath , "*.ird" , SearchOption . TopDirectoryOnly ) ;
403+ }
404+
405+ // Warn if no files are found
406+ if ( ! irdFiles . Any ( ) )
407+ {
408+ if ( opt . Recurse )
409+ Console . Error . WriteLine ( $ "No IRDs found in { irdPath } (ensure .ird extension)") ;
410+ else
411+ Console . Error . WriteLine ( $ "No IRDs found in { irdPath } (ensure .ird extension, or try use -r)") ;
412+ continue ;
413+ }
414+
415+ // Rename all IRD files found
416+ foreach ( string file in irdFiles . OrderBy ( x => x ) )
417+ {
418+ try
419+ {
420+ RenameIRD ( file , datfile , serial : opt . Serial , crc : opt . CRC , verbose : opt . Verbose ) ;
421+ }
422+ catch ( Exception e )
423+ {
424+ Console . Error . WriteLine ( e ) ;
425+ }
426+ }
427+
428+ }
429+ else
430+ {
431+ // Check that given file exists
432+ if ( ! File . Exists ( irdPath ) )
433+ {
434+ Console . Error . WriteLine ( $ "IRD not found: { irdPath } ") ;
435+ continue ;
436+ }
437+
438+ // Rename provided IRD path
439+ try
440+ {
441+ RenameIRD ( irdPath , datfile , serial : opt . Serial , crc : opt . CRC , verbose : opt . Verbose ) ;
442+ }
443+ catch ( Exception e )
444+ {
445+ Console . Error . WriteLine ( e ) ;
446+ }
447+ }
448+ }
449+
450+ break ;
451+
333452 // Unknown command
334453 default :
335454 break ;
@@ -895,6 +1014,65 @@ public static string ISO2IRD(string isoPath, string irdPath = null, string hexKe
8951014 }
8961015 }
8971016
1017+ public static XDocument DatParser ( string datpath = null )
1018+ {
1019+ try
1020+ {
1021+ if ( ! File . Exists ( datpath ) )
1022+ return null ;
1023+
1024+ return XDocument . Load ( datpath ) ;
1025+ }
1026+ catch
1027+ {
1028+ return null ;
1029+ }
1030+ }
1031+
1032+ public static string GetDatFilename ( IRD ird , XDocument datfile )
1033+ {
1034+ string crc32 = ird . UID . ToString ( "X8" ) . ToLower ( ) ;
1035+ XElement node = datfile . Root . Elements ( "game" ) . Where ( e => e . Element ( "rom" ) . Attribute ( "crc" ) . Value == crc32 ) . FirstOrDefault ( ) ?? throw new ArgumentException ( "Cannot find ISO in redump DAT" ) ;
1036+ return node . Attribute ( "name" ) . Value ;
1037+ }
1038+
1039+ public static void RenameIRD ( string irdPath , XDocument datfile , bool serial = false , bool crc = false , bool verbose = false )
1040+ {
1041+ IRD ird = IRD . Read ( irdPath ) ;
1042+
1043+ if ( ird . ExtraConfig != 0x0001 )
1044+ throw new ArgumentException ( $ "{ irdPath } is not a redump-style IRD") ;
1045+
1046+ string filename = GetDatFilename ( ird , datfile ) ;
1047+ if ( filename == null )
1048+ throw new ArgumentException ( $ "Cannot determine DAT filename for { irdPath } ") ;
1049+ if ( serial )
1050+ filename += $ " [{ ird . TitleID [ ..4 ] } -{ ird . TitleID [ 4 ..9 ] } ]";
1051+ if ( crc )
1052+ filename += $ " [{ ird . UID : X8} ]";
1053+
1054+ // Rename irdPath to filename
1055+ string directory = Path . GetDirectoryName ( Path . GetFullPath ( filename ) ) ;
1056+ string filepath ;
1057+ if ( ! string . IsNullOrEmpty ( directory ) )
1058+ filepath = Path . Combine ( Path . GetDirectoryName ( irdPath ) , filename + ".ird" ) ;
1059+ else
1060+ filepath = filename + ".ird" ;
1061+
1062+ // Rename IRD to new name
1063+ if ( irdPath != filepath )
1064+ {
1065+ if ( verbose )
1066+ Console . WriteLine ( $ "Renaming { Path . GetFileName ( irdPath ) } to { Path . GetFileName ( filepath ) } ") ;
1067+ File . Move ( irdPath , filepath ) ;
1068+ }
1069+ else
1070+ {
1071+ if ( verbose )
1072+ Console . WriteLine ( $ "Skipping { Path . GetFileName ( irdPath ) } , already named correctly") ;
1073+ }
1074+ }
1075+
8981076 #endregion
8991077
9001078 #region Helper Functions
0 commit comments