0
|
1 ???using System; |
|
2 using System.Diagnostics; |
|
3 using System.Globalization; |
|
4 using System.IO; |
|
5 using System.Windows.Forms; |
|
6 |
|
7 namespace AutoCRCheck { |
|
8 static class Program { |
|
9 static Boolean DeleteBad = false; |
|
10 static String RenameBadSuffix = null; |
|
11 static Boolean WaitOnCompletion = false; |
|
12 static Boolean PrintMissing = false; |
|
13 static Boolean PrintMismatch = false; |
|
14 static UInt32 DirectoriesChecked = 0; |
|
15 static UInt32 ListsChecked = 0; |
|
16 static UInt32 FilesMissing = 0; |
|
17 static UInt32 FilesBad = 0; |
|
18 static UInt32 FilesGood = 0; |
|
19 [STAThread] |
|
20 static int Main(String[] args) { |
|
21 String rootdirectory = null; |
|
22 Console.Error.WriteLine("UCIS AutoCRCheck (c) 2014 Ivo Smits <Ivo@UCIS.nl>"); |
|
23 Console.Error.WriteLine("See http://wiki.ucis.nl/AutoCRCheck for more information"); |
|
24 for (int i = 0; i < args.Length; i++) { |
|
25 if (args[i].StartsWith("-")) { |
|
26 switch (args[i].Substring(1)) { |
|
27 case "d": |
|
28 case "delete": |
|
29 DeleteBad = true; |
|
30 break; |
|
31 case "r": |
|
32 case "rename": |
|
33 RenameBadSuffix = args[++i]; |
|
34 break; |
|
35 case "w": |
|
36 case "wait": |
|
37 WaitOnCompletion = true; |
|
38 break; |
|
39 case "printmissing": |
|
40 PrintMissing = true; |
|
41 break; |
|
42 case "p": |
|
43 case "printmismatch": |
|
44 PrintMismatch = true; |
|
45 break; |
|
46 case "help": |
|
47 case "h": |
|
48 PrintCommandlineHelp(Console.Out); |
|
49 return -1; |
|
50 default: |
|
51 Console.Error.WriteLine("Error: unknown command line argument: " + args[i]); |
|
52 PrintCommandlineHelp(Console.Error); |
|
53 return -1; |
|
54 } |
|
55 } else { |
|
56 if (rootdirectory != null) { |
|
57 Console.Error.WriteLine("Error: duplicate root directory specified."); |
|
58 return -1; |
|
59 } |
|
60 rootdirectory = args[i]; |
|
61 } |
|
62 } |
|
63 if (rootdirectory == null) { |
|
64 Console.Error.WriteLine("Root directory not set, waiting for GUI input..."); |
|
65 try { |
|
66 using (new Control()) ; |
|
67 } catch { |
|
68 Console.Error.WriteLine("It looks like you're trying to run this application in a non-GUI session. Luckily we're handling this nearly fatal error for you."); |
|
69 PrintCommandlineHelp(Console.Error); |
|
70 return -1; |
|
71 } |
|
72 using (frmOptions options = new frmOptions()) { |
|
73 DialogResult result = options.ShowDialog(); |
|
74 if (result != DialogResult.OK) return -2; |
|
75 rootdirectory = options.RootDirectory; |
|
76 DeleteBad = options.MismatchDelete; |
|
77 RenameBadSuffix = options.MismatchRenameSuffix; |
|
78 WaitOnCompletion = true; |
|
79 } |
|
80 |
|
81 } |
|
82 if (!Directory.Exists(rootdirectory)) { |
|
83 Console.Error.WriteLine("Error: root directory not found."); |
|
84 return -1; |
|
85 } |
|
86 rootdirectory = Path.GetFullPath(rootdirectory); |
|
87 ScanDirectory(rootdirectory); |
|
88 String report = String.Format( |
|
89 "{0} directories scanned.\n{1} .SFV files processed\n{2} files were missing\n{3} files had a mismatching checksum, they have been dealt with.\n{4} files are good", |
|
90 DirectoriesChecked, ListsChecked, FilesMissing, FilesBad, FilesGood); |
|
91 Console.Error.WriteLine(report); |
|
92 Console.Error.WriteLine("Done."); |
|
93 if (WaitOnCompletion) { |
|
94 MessageBox.Show("All files have been checked." + Environment.NewLine + report, "UCIS AutoCRCheck", MessageBoxButtons.OK, MessageBoxIcon.Information); |
|
95 } |
|
96 return 0; |
|
97 } |
|
98 static void PrintCommandlineHelp(TextWriter w) { |
|
99 w.WriteLine("UCIS AutoCRCheck recursively checks directories for .SFV files and handles mismatching checksums as instructed."); |
|
100 w.WriteLine("Usage: AutoCRCheck.exe [options] [root directory]"); |
|
101 w.WriteLine("Possible options:"); |
|
102 w.WriteLine(" -delete Delete mismatching files"); |
|
103 w.WriteLine(" -rename [suffix] Rename mismatching files, append [suffix] to filename"); |
|
104 w.WriteLine(" -printmissing Print full path of missing files"); |
|
105 w.WriteLine(" -printmismatch Print full path of mismatched files"); |
|
106 w.WriteLine(" -wait Wait for user input when done"); |
|
107 w.WriteLine(" -help Display this message"); |
|
108 w.WriteLine(); |
|
109 w.WriteLine("Examples:"); |
|
110 w.WriteLine(" AutoCRCheck.exe . -rename .badcrc"); |
|
111 w.WriteLine(" AutoCRCheck.exe . -printmismatch > badfiles.txt"); |
|
112 } |
|
113 static void ScanDirectory(String dir) { |
|
114 DirectoriesChecked++; |
|
115 Console.Error.WriteLine("Directory: {0}", dir); |
|
116 foreach (String sfv in Directory.GetFiles(dir, "*.sfv")) { |
|
117 ListsChecked++; |
|
118 Console.Error.WriteLine("Checksum file: {0}", Path.GetFileName(sfv)); |
|
119 using (TextReader reader = File.OpenText(Path.Combine(dir, sfv))) { |
|
120 while (true) { |
|
121 String line = reader.ReadLine(); |
|
122 if (line == null) break; |
|
123 if (line.Length == 0 || line[0] == ';') continue; |
|
124 int lastspace = line.LastIndexOf(' '); |
|
125 if (lastspace == -1) continue; |
|
126 String file = line.Substring(0, lastspace).TrimEnd(' '); |
|
127 String cksumstr = line.Substring(lastspace); |
|
128 UInt32 goodcksum; |
|
129 if (!UInt32.TryParse(cksumstr, NumberStyles.HexNumber, null, out goodcksum)) { |
|
130 Console.Error.WriteLine("Failed to decode checksum string: {0}", cksumstr); |
|
131 continue; |
|
132 } |
|
133 String filepath = Path.Combine(dir, file); |
|
134 if (!File.Exists(filepath)) { |
|
135 FilesMissing++; |
|
136 Console.Error.WriteLine("File not found: {0}", file); |
|
137 if (PrintMissing) Console.Out.WriteLine(filepath); |
|
138 continue; |
|
139 } |
|
140 UInt32 realcksum; |
|
141 Console.Error.Write("Checking file: {0}... ", file); |
|
142 using (Stream input = File.OpenRead(filepath)) realcksum = CRCCalculate(input); |
|
143 if (goodcksum != realcksum) { |
|
144 FilesBad++; |
|
145 Console.Error.WriteLine("Mismatch."); |
|
146 if (PrintMismatch) Console.Out.WriteLine(filepath); |
|
147 if (RenameBadSuffix != null) { |
|
148 File.Move(filepath, filepath + RenameBadSuffix); |
|
149 } else if (DeleteBad) { |
|
150 File.Delete(filepath); |
|
151 } |
|
152 } else { |
|
153 FilesGood++; |
|
154 Console.Error.WriteLine("Good."); |
|
155 } |
|
156 } |
|
157 } |
|
158 } |
|
159 foreach (String subdir in Directory.GetDirectories(dir)) { |
|
160 ScanDirectory(Path.Combine(dir, subdir)); |
|
161 } |
|
162 } |
|
163 static readonly UInt32[] CRCTable = CRCGenerateTable(); |
|
164 static UInt32[] CRCGenerateTable() { |
|
165 UInt32[] table = new UInt32[256]; |
|
166 for (UInt32 v = 0; v < 256; v++) { |
|
167 UInt32 e = CRCReflect(v, 8) << 24; |
|
168 for (int i = 0; i < 8; i++) { |
|
169 if ((e & 0x80000000L) == 0) e <<= 1; |
|
170 else e = (e << 1) ^ 0x04C11DB7; |
|
171 } |
|
172 table[v] = CRCReflect(e, 32); |
|
173 } |
|
174 return table; |
|
175 } |
|
176 static UInt32 CRCReflect(UInt32 r, int v) { |
|
177 UInt32 ret = 0; |
|
178 for (int i = 1; i < v + 1; i++, r >>= 1) if ((r & 1) != 0) ret |= 1u << (v - i); |
|
179 return ret; |
|
180 } |
|
181 static uint CRCCalculate(Stream input) { |
|
182 uint c = 0xffffffff; |
|
183 Byte[] buffer = new Byte[Math.Min(1024, input.Length)]; |
|
184 while (true) { |
|
185 int n = input.Read(buffer, 0, buffer.Length); |
|
186 if (n <= 0) break; |
|
187 for (int i = 0; i < n; i++) c = CRCTable[(c ^ buffer[i]) & 0xFF] ^ (c >> 8); |
|
188 } |
|
189 return c ^ 0xffffffff; |
|
190 } |
|
191 } |
|
192 } |