44using System . Drawing ;
55using System . IO ;
66using System . Threading . Tasks ;
7+
78namespace ScsExtractorGui
89{
910 public class MainForm : Form
@@ -16,12 +17,14 @@ public class MainForm : Form
1617 private ProgressBar ? progressBar ;
1718 private TabControl ? tabControl ;
1819 private Process ? currentProcess ;
20+
1921 private readonly Color BgColor = Color . FromArgb ( 28 , 28 , 28 ) ;
2022 private readonly Color ControlBg = Color . FromArgb ( 45 , 45 , 45 ) ;
2123 private readonly Color AccentColor = Color . FromArgb ( 0 , 120 , 212 ) ;
2224 private readonly Color TextColor = Color . FromArgb ( 255 , 255 , 255 ) ;
2325 private readonly Color SecondaryText = Color . FromArgb ( 160 , 160 , 160 ) ;
2426 private readonly string extractorPath = "extractor.exe" ;
27+
2528 public MainForm ( )
2629 {
2730 this . Text = "SCS Extractor GUI — Complete Edition" ;
@@ -32,12 +35,14 @@ public MainForm()
3235 this . StartPosition = FormStartPosition . CenterScreen ;
3336 this . FormClosing += ( s , e ) => KillProcessTree ( ) ;
3437 InitializeComponents ( ) ;
38+
3539 if ( ! File . Exists ( extractorPath ) )
3640 {
3741 MessageBox . Show ( "extractor.exe not found in application directory!\n Please place extractor.exe in the same folder as this GUI." ,
3842 "Missing Extractor" , MessageBoxButtons . OK , MessageBoxIcon . Warning ) ;
3943 }
4044 }
45+
4146 private void InitializeComponents ( )
4247 {
4348 int left = 20 , width = 690 ;
@@ -49,13 +54,16 @@ private void InitializeComponents()
4954 BackColor = ControlBg ,
5055 ForeColor = TextColor
5156 } ;
57+
5258 TabPage basicTab = new TabPage ( "Basic Options" ) { BackColor = BgColor } ;
5359 AddBasicControls ( basicTab ) ;
5460 TabPage advancedTab = new TabPage ( "Advanced" ) { BackColor = BgColor } ;
5561 AddAdvancedControls ( advancedTab ) ;
5662 TabPage hashfsTab = new TabPage ( "HashFS" ) { BackColor = BgColor } ;
5763 AddHashFSControls ( hashfsTab ) ;
64+
5865 tabControl . TabPages . AddRange ( new TabPage [ ] { basicTab , advancedTab , hashfsTab } ) ;
66+
5967 progressBar = new ProgressBar
6068 {
6169 Location = new Point ( left , 870 ) ,
@@ -64,14 +72,18 @@ private void InitializeComponents()
6472 Visible = false ,
6573 MarqueeAnimationSpeed = 30
6674 } ;
75+
6776 btnStart = CreateButton ( "START EXTRACTION" , left , 890 , 340 , 45 , AccentColor ) ;
6877 btnStart . Font = new Font ( this . Font , FontStyle . Bold ) ;
6978 btnStart . Click += RunExtractor ;
79+
7080 btnStop = CreateButton ( "STOP" , 370 , 890 , 340 , 45 , Color . FromArgb ( 190 , 30 , 30 ) ) ;
7181 btnStop . Enabled = false ;
7282 btnStop . Click += ( s , e ) => KillProcessTree ( ) ;
83+
7384 this . Controls . AddRange ( new Control [ ] { tabControl , progressBar , btnStart , btnStop } ) ;
7485 }
86+
7587 private void AddBasicControls ( TabPage page )
7688 {
7789 int left = 20 , width = 630 , y = 20 ;
@@ -89,6 +101,7 @@ private void AddBasicControls(TabPage page)
89101 } ;
90102 page . Controls . AddRange ( new Control [ ] { txtPath , btnBrowse } ) ;
91103 y += 35 ;
104+
92105 AddLabel ( page , "Destination Folder (-d):" , left , y ) ;
93106 y += 20 ;
94107 txtDest = CreateTextBox ( left , y , 500 ) ;
@@ -101,6 +114,7 @@ private void AddBasicControls(TabPage page)
101114 } ;
102115 page . Controls . AddRange ( new Control [ ] { txtDest , btnDestBrowse } ) ;
103116 y += 45 ;
117+
104118 AddHeader ( page , "EXTRACTION MODES" , left , y ) ;
105119 y += 25 ;
106120 rbModeNormal = CreateRadioButton ( "Normal Extraction" , left , y , true ) ;
@@ -112,18 +126,24 @@ private void AddBasicControls(TabPage page)
112126 rbModeTree = CreateRadioButton ( "Show Tree (--tree)" , left + 200 , y , false ) ;
113127 page . Controls . AddRange ( new Control [ ] { rbModeListEntries , rbModeTree } ) ;
114128 y += 45 ;
129+
115130 AddHeader ( page , "FILTERING" , left , y ) ;
116131 y += 25 ;
132+
117133 AddLabel ( page , "Filter Patterns (-f):" , left , y ) ;
118134 y += 20 ;
119135 txtFilter = CreateTextBox ( left , y , width ) ;
136+ page . Controls . Add ( txtFilter ) ;
120137 AddLabel ( page , "Examples: *volvo*,*scania* | r/\\ .pmg$/" , left + 5 , y + 25 , true , 10 ) ;
121138 y += 55 ;
139+
122140 AddLabel ( page , "Partial Extraction (-p):" , left , y ) ;
123141 y += 20 ;
124142 txtPartial = CreateTextBox ( left , y , width ) ;
143+ page . Controls . Add ( txtPartial ) ;
125144 AddLabel ( page , "Examples: /def,/map | /locale" , left + 5 , y + 25 , true , 10 ) ;
126145 y += 55 ;
146+
127147 AddLabel ( page , "Paths File (-P):" , left , y ) ;
128148 y += 20 ;
129149 txtPathsFile = CreateTextBox ( left , y , 500 ) ;
@@ -135,6 +155,7 @@ private void AddBasicControls(TabPage page)
135155 } ;
136156 page . Controls . AddRange ( new Control [ ] { txtPathsFile , btnPathsBrowse } ) ;
137157 y += 45 ;
158+
138159 chkAll = CreateCheckBox ( "Extract All Archives in Directory (-a)" , left , y ) ;
139160 y += 25 ;
140161 chkSeparate = CreateCheckBox ( "Separate Folders for Multiple Archives (-S)" , left , y ) ;
@@ -144,17 +165,22 @@ private void AddBasicControls(TabPage page)
144165 chkQuiet = CreateCheckBox ( "Quiet Mode (-q)" , left , y ) ;
145166 y += 25 ;
146167 chkDryRun = CreateCheckBox ( "Dry Run (--dry-run)" , left , y ) ;
168+
147169 page . Controls . AddRange ( new Control [ ] { chkAll , chkSeparate , chkSkip , chkQuiet , chkDryRun } ) ;
148170 }
171+
149172 private void AddAdvancedControls ( TabPage page )
150173 {
151174 int left = 20 , width = 630 , y = 20 ;
175+
152176 AddHeader ( page , "ADVANCED OPTIONS" , left , y ) ;
153177 y += 40 ;
154178 AddLabel ( page , "Manual Flags (for unsupported options):" , left , y ) ;
155179 y += 25 ;
156180 txtManual = CreateTextBox ( left , y , width ) ;
181+ page . Controls . Add ( txtManual ) ;
157182 y += 45 ;
183+
158184 AddHeader ( page , "OUTPUT LOG" , left , y ) ;
159185 y += 30 ;
160186 txtLog = new TextBox
@@ -171,6 +197,7 @@ private void AddAdvancedControls(TabPage page)
171197 } ;
172198 page . Controls . Add ( txtLog ) ;
173199 }
200+
174201 private void AddHashFSControls ( TabPage page )
175202 {
176203 int left = 20 , width = 630 , y = 20 ;
@@ -211,6 +238,7 @@ private void AddHashFSControls(TabPage page)
211238 } ;
212239 page . Controls . Add ( infoLabel ) ;
213240 }
241+
214242 private void AddHeader ( TabPage page , string text , int x , int y )
215243 {
216244 page . Controls . Add ( new Label
@@ -222,6 +250,7 @@ private void AddHeader(TabPage page, string text, int x, int y)
222250 Font = new Font ( "Segoe UI" , 9 , FontStyle . Bold )
223251 } ) ;
224252 }
253+
225254 private void AddLabel ( TabPage page , string text , int x , int y , bool small = false , int fontSize = 10 )
226255 {
227256 page . Controls . Add ( new Label
@@ -233,6 +262,7 @@ private void AddLabel(TabPage page, string text, int x, int y, bool small = fals
233262 Font = new Font ( "Segoe UI" , small ? 8 : fontSize )
234263 } ) ;
235264 }
265+
236266 private TextBox CreateTextBox ( int x , int y , int w )
237267 {
238268 return new TextBox
@@ -244,6 +274,7 @@ private TextBox CreateTextBox(int x, int y, int w)
244274 BorderStyle = BorderStyle . FixedSingle
245275 } ;
246276 }
277+
247278 private CheckBox CreateCheckBox ( string text , int x , int y )
248279 {
249280 return new CheckBox
@@ -255,6 +286,7 @@ private CheckBox CreateCheckBox(string text, int x, int y)
255286 ForeColor = TextColor
256287 } ;
257288 }
289+
258290 private RadioButton CreateRadioButton ( string text , int x , int y , bool checkedState )
259291 {
260292 return new RadioButton
@@ -267,6 +299,7 @@ private RadioButton CreateRadioButton(string text, int x, int y, bool checkedSta
267299 Checked = checkedState
268300 } ;
269301 }
302+
270303 private Button CreateButton ( string text , int x , int y , int w , int h , Color bg )
271304 {
272305 return new Button
@@ -280,6 +313,7 @@ private Button CreateButton(string text, int x, int y, int w, int h, Color bg)
280313 Cursor = Cursors . Hand
281314 } ;
282315 }
316+
283317 private void Log ( string msg )
284318 {
285319 if ( txtLog ! . InvokeRequired )
@@ -291,30 +325,37 @@ private void Log(string msg)
291325 txtLog . SelectionStart = txtLog . Text . Length ;
292326 txtLog . ScrollToCaret ( ) ;
293327 }
328+
294329 private async void RunExtractor ( object ? sender , EventArgs ? e )
295330 {
296331 if ( string . IsNullOrEmpty ( txtPath ? . Text ) )
297332 {
298333 MessageBox . Show ( "Please select an input file/folder." , "Missing Input" , MessageBoxButtons . OK , MessageBoxIcon . Warning ) ;
299334 return ;
300335 }
336+
301337 if ( ! File . Exists ( extractorPath ) )
302338 {
303339 MessageBox . Show ( "extractor.exe not found!" , "Error" , MessageBoxButtons . OK , MessageBoxIcon . Error ) ;
304340 return ;
305341 }
342+
306343 btnStart ! . Enabled = false ;
307344 btnStop ! . Enabled = true ;
308345 progressBar ! . Visible = true ;
309346 txtLog ! . Clear ( ) ;
347+
310348 string args = BuildArguments ( ) ;
311349 Log ( $ ">> CMD: { extractorPath } { args } \r \n ") ;
350+
312351 await Task . Run ( ( ) => RunProcess ( args ) ) ;
352+
313353 btnStart . Enabled = true ;
314354 btnStop . Enabled = false ;
315355 progressBar . Visible = false ;
316356 Log ( "\r \n >> Operation Finished." ) ;
317357 }
358+
318359 private string BuildArguments ( )
319360 {
320361 string args = $ "\" { txtPath ! . Text } \" ";
@@ -330,27 +371,37 @@ private string BuildArguments()
330371 args += " --list-entries" ;
331372 else if ( rbModeTree ! . Checked )
332373 args += " --tree" ;
374+
333375 if ( chkAll ! . Checked ) args += " -a" ;
334376 if ( chkSeparate ! . Checked ) args += " -S" ;
335377 if ( chkSkip ! . Checked ) args += " -s" ;
336378 if ( chkQuiet ! . Checked ) args += " -q" ;
337379 if ( chkDryRun ! . Checked ) args += " --dry-run" ;
380+
338381 if ( ! string . IsNullOrWhiteSpace ( txtFilter ? . Text ) )
339382 args += $ " -f=\" { txtFilter . Text . Trim ( ) } \" ";
383+
340384 if ( ! string . IsNullOrWhiteSpace ( txtPartial ? . Text ) )
341385 args += $ " -p=\" { txtPartial . Text . Trim ( ) } \" ";
386+
342387 if ( ! string . IsNullOrWhiteSpace ( txtPathsFile ? . Text ) && File . Exists ( txtPathsFile . Text ) )
343388 args += $ " -P \" { txtPathsFile . Text } \" ";
389+
344390 if ( chkDeep ! . Checked ) args += " -D" ;
345391 if ( chkRaw ! . Checked ) args += " --raw" ;
392+
346393 if ( ! string . IsNullOrWhiteSpace ( txtSalt ? . Text ) )
347394 args += $ " --salt={ txtSalt . Text . Trim ( ) } ";
395+
348396 if ( ! string . IsNullOrWhiteSpace ( txtAdditionalFile ? . Text ) && File . Exists ( txtAdditionalFile . Text ) )
349397 args += $ " --additional \" { txtAdditionalFile . Text } \" ";
398+
350399 if ( ! string . IsNullOrWhiteSpace ( txtManual ? . Text ) )
351400 args += $ " { txtManual . Text . Trim ( ) } ";
401+
352402 return args ;
353403 }
404+
354405 private void RunProcess ( string args )
355406 {
356407 try
@@ -368,8 +419,10 @@ private void RunProcess(string args)
368419 StandardOutputEncoding = System . Text . Encoding . UTF8 ,
369420 StandardErrorEncoding = System . Text . Encoding . UTF8
370421 } ;
422+
371423 currentProcess . OutputDataReceived += ( s , a ) => { if ( a . Data != null ) Log ( a . Data ) ; } ;
372424 currentProcess . ErrorDataReceived += ( s , a ) => { if ( a . Data != null ) Log ( "ERR: " + a . Data ) ; } ;
425+
373426 currentProcess . Start ( ) ;
374427 currentProcess . BeginOutputReadLine ( ) ;
375428 currentProcess . BeginErrorReadLine ( ) ;
@@ -388,10 +441,12 @@ private void RunProcess(string args)
388441 currentProcess = null ;
389442 }
390443 }
444+
391445 private void KillProcessTree ( )
392446 {
393447 if ( currentProcess == null || currentProcess . HasExited )
394448 return ;
449+
395450 try
396451 {
397452 currentProcess . Kill ( ) ;
@@ -407,6 +462,7 @@ private void KillProcessTree()
407462 UseShellExecute = false
408463 } ) ? . WaitForExit ( ) ;
409464 }
465+
410466 Log ( "\r \n [!] PROCESS TERMINATED." ) ;
411467 }
412468 catch ( Exception ex )
@@ -418,6 +474,7 @@ private void KillProcessTree()
418474 currentProcess = null ;
419475 }
420476 }
477+
421478 [ STAThread ]
422479 static void Main ( )
423480 {
0 commit comments