1 /*---------------------------------------------------------------------------*
2 
3   Copyright 2010-2012 Nintendo.  All rights reserved.
4 
5   These coded instructions, statements, and computer programs contain
6   proprietary information of Nintendo of America Inc. and/or Nintendo
7   Company Ltd., and are protected by Federal copyright law.  They may
8   not be disclosed to third parties or copied or duplicated in any form,
9   in whole or in part, without the prior written consent of Nintendo.
10 
11  *---------------------------------------------------------------------------*/
12 using System.Collections.Generic;
13 using System;
14 using System.IO;
15 using System.Runtime.InteropServices;
16 using System.Text;
17 using System.Globalization;
18 
19 namespace CafeX
20 {
21     public enum CAFEX_ERROR
22         {
23             OK = 0,
24             UPDATE_BRINGUP_BOOT1_UPDATE = 0,
25             RUN_SOFT_LAUNCH_WITH_DISCRUN = 1,
26             ON_RECURSIVE_CALL = 1,
27             SETBOOTMODE_BAD_MODE = 1,
28             MASTERING_AOC_INVALID_PARAMETERS = 1,
29             RUN_RPX_DOESNT_EXIST = 2,
30             HOSTCHECKVERSION_NO_HOSTBRIDGE = 2,
31             HOSTCHECKVERSION_FSEMUL_FAILED = 2,
32             HOSTCHECKVERSION_OLD_VERSIONS = 2,
33             BOOTMODEDETECT_FSEMUL_FAILED = 2,
34             MASTERING_DOWNLOAD_MASTERPART_NOT_FOUND = 2,
35             MASTERING_DOWNLOAD_WUMA_NOT_FOUND = 2,
36             MASTERING_DISC_MASTER_ARCHIVE_NOT_PROVIDED = 3,
37             MASTERING_AOC_CONTENT_SECTION_NOT_FOUND = 3,
38             MASTERING_DOWNLOAD_INVALID_WUMA = 4,
39             MASTERING_AOC_TOO_MANY_CONTENT_DIRS = 4,
40             MASTERING_DISC_INVALID_MASTER_ARCHIVE = 3,
41             MASTERING_DOWNLOAD_INVALID_OUTPUT_BASE_DIR = 5,
42             RUN_HOSTSTOP_RVAL_NON_ZERO = 5,
43             RUN_NO_RPX_SPECIFIED = 6,
44             RUN_TITLE_ID_NOT_FOUND_IN_MLC = 7,
45             RUN_FASTLAUNCH_FAILED = 7,
46             MASTERING_DOWNLOAD_INVALID_TEMP_DIR = 7,
47             MASTERING_DISC_INVALID_TEMP_DIR = 7,
48             RUN_CANT_FIND_OS_DIRECTORY = 8,
49             RUN_OS_NOT_PRESENT = 8,
50             RUN_DASH_R_WITH_NO_PREVIOUS_LAUNCH_INFO = 9,
51             RUN_DASH_R_CANT_FIND_APPLICATION = 9,
52             RUN_DISCRUN_NAND_UNABLE_TO_FIND_APPLICATION = 9,
53             RUN_UPDATE_ARGLIST_FAILED = 11,
54             RUN_CHECKTITLEID_FAILED = 12,
55             RUN_UPDATE_APP_XML_FAILED = 12,
56             RUN_UPDATE_COS_XML_FAILED = 12,
57             RUN_UPDATE_META_XML_FAILED = 12,
58             RUN_UPDATE_SYSTEM_XML_FAILED = 12,
59             MASTERING_ERROR = 14,
60             RUN_BAD_DEBUGGER = 20,
61             RUN_BAD_DEBUGGER_PORT = 21,
62             RUN_TEST_MODE_WITH_NAND_BOOT = 25,
63             RUN_PCFSSERVER_SYNC_FAILED = 30,
64             BOOTRUN_MAKEBSF_FAILED = 30,
65             BOOTRUN_MAKEDLF_FAILED = 31,
66             CAFEDEVRUN_CAFEMAKEDLF_FAILED = 31,
67             CAFEDEVRUN_NO_IMAGE_FILE = 50,
68             CAFEDEVRUN_CANT_FIND_APP_XML = 53,
69             RUN_KILL_RESTART_FAILED = 54,
70             CHECKNAME_FAILED = 55,
71 
72 
73             // CafeX only
74             UNEXPECTED = -1,
75             NO_SDK_HOSTBRIDGE_ENV_VARS = -2,
76             BAD_COMMAND = -3,
77             BAD_ARGUMENT = -4,
78             NO_DEVKIT_VARS = -5,
79             NO_SESSIONMANAGER_DLL = -6,
80             RUN_SPACES_IN_CAFE_ROOT = -7,
81             RUN_COULDNT_PARSE_SDK_VERSION_HEADER = -8,
82             RUN_NO_SDK_VERSION_HEADER = -9,
83             RUN_BAD_H_ARGUMENT = -10,
84             RUN_INVALID_OPTION = -11,
85             RUN_NO_TEMP_VAR = -12,
86             BOOTRUN_NO_TEMP_VAR = -13,
87             RUN_NON_MULTI_DEBUGGER = -14,
88             UPDATE_INVALID_OPTION = -15,
89             SETBOOTMODE_FAILED = -16,
90             RUN_TITLE_ID_REQUIRED = -17,
91             SYNCTOOL_FAILED = -18,
92             RECOVERY_FAILED = -19,
93             CLEARDATA_FAILED = -20,
94             MIONURL_FAILED = -21,
95             IMAGEUPLOADER_FAILED = -22,
96             SYSLOG_LEVEL_SET_FAILED = -23,
97             UPDATE_FAILED = -24,
98             UPLOADED_IMAGE_OS_MISMATCH = -25,
99             RUN_LAUNCH_FAILED = -26,
100             MIONPS_ERROR = -27, //mionps will always return an error code of 17 on failure, so we must check the string associated with the error.
101             BOOT_SYNC_FAILED = -28,
102             TITLE_SYNC_FAILED = -29,
103 
104             // Unmapped errors - should return 2 until BASH scripts map them to other values.
105             RUN_MEMMAP_NOT_SUPPORTED = 2,
106             RUN_BAD_TITLE_ID = 2,
107             RUN_NO_CAFE_DATA_DIR = 2,
108             RUN_D_OPTION_USED = 2,
109             ON_DATA_DIR_MISSING = 2,
110         }
111 
112     internal class CafeXReturn
113     {
114         public CAFEX_ERROR error;
115         public int appExitCode;
116 
117         /// <summary>
118         /// Constructor for CAFEX_ERROR class.  Sets the Error to the user-specified error.
119         /// Exit code = 0.
120         /// </summary>
121         /// <param name="err"></param>
CafeXReturn(CAFEX_ERROR err)122         public CafeXReturn(CAFEX_ERROR err)
123         {
124             error = err;
125             appExitCode = 0;
126         }
127         /// <summary>
128         /// Constructor for CAFEX_ERROR class.  Sets the application error code
129         /// according to the app's actual exit code.  Error = OK.
130         /// </summary>
131         /// <param name="exitcode"></param>
CafeXReturn(int exitcode)132         public CafeXReturn(int exitcode)
133         {
134             error = CAFEX_ERROR.OK;
135             appExitCode = exitcode;
136         }
137 
138         /// <summary>
139         /// Constructor for CAFEX_ERROR class.
140         /// </summary>
141         /// <param name="err">The Error CafeX should return</param>
142         /// <param name="exitcode">The exit code of the application</param>
CafeXReturn(CAFEX_ERROR err, int exitcode)143         public CafeXReturn(CAFEX_ERROR err, int exitcode)
144         {
145             error = err;
146             appExitCode = exitcode;
147         }
148 
CafeXReturn()149         public CafeXReturn()
150         {
151             error = CAFEX_ERROR.UNEXPECTED;
152             appExitCode = -1;
153         }
154     }
155 
156     partial class Program
157     {
158 
159 #if TIMER
160         public static PerfTimer CafeXSetupTime = new PerfTimer("CafeXSetupTime");
161 #endif
162 
163         private enum TargetEncoding
164         {
165             ConsoleIn,
166             ConsoleOut,
167             CafeOut
168         }
169 
170         // This variable is set if cafex is called with '-q'.
171         internal static bool quiet_mode = false;
172         // this variable is set if cafex is called with -noinput
173         internal static bool no_console = false;
174         // The cafex command to execute.
175         internal static string command = string.Empty;
176 
177         // Global software and firmware versions
178         internal static int sw_ver_flat = 0;
179         internal static int fw_ver_flat = 0;
180 
181         internal static readonly uint RETRY_ATTEMPS = 3;
182 
183         internal const int DEVKIT_HELP_REQUEST_TIMEOUT = 5000;
184         internal const int DEVKIT_HELP_RESPONSE_TIMEOUT = 5000;
185 
186         internal const int COSDEBUG_FASTLAUNCH_RETRIES = 3;
187         internal const int COSDEBUG_FASTLAUNCH_TIMEOUT = 10000; // 10 seconds
188         internal const int COSDEBUG_FASTLAUNCH_DELAY   = 2000;  // 2 seconds
189 
190         internal const int COSDEBUG_KILLRESTART_RETRIES = 1;
191         internal const int COSDEBUG_KILLRESTART_TIMEOUT = 1000; // 1 second
192         internal const int COSDEBUG_KILLRESTART_DELAY   = 1000; // 1 second
193 
194         internal const int KILLRESTART_SOFTLAUNCH_TIMEOUT = 10000; // 10 seconds
195 
196         internal static readonly TimeSpan RETRY_TIMESPAN = new TimeSpan(0, 0, 1);
197 
198         internal static readonly TimeSpan COS_REBOOT_DELAY = new TimeSpan(0, 0, 5);
199 
200         internal static readonly TimeSpan COS_READY_DELAY = new TimeSpan(0, 0, 12);
201 
202         static private string PERMISSION_ERROR_TEXT = "Check your permission to write to the file's directory, and that the file name provided does not conflict with an existing directory name.";
203 
204         static public Encoding cafeOutEncoding;
205 
206 //        static private bool CALLED_FROM_CYGWIN = false;
207         /// <summary>
208         /// Cleans up input argument array by removing any empty ones, and
209         /// breaking up those that are not spaced out properly (eg. -t0x00...)
210         /// which makes the argument parsing logic more reliable.
211         /// </summary>
212         /// <param name="args">Input argument array of strings.</param>
213         /// <returns>New array of arguments.</returns>
CleanupArguments(string[] args)214         static private string[] CleanupArguments(string[] args)
215         {
216 #if DEBUG
217             Log.WriteLine("CleanEmptArguments started.");
218 #endif
219 
220             List<string> newList = new List<string>();
221             int rpxIndex = int.MaxValue;
222             bool rpxFound = false;
223 
224             for (int counter = 0; counter < args.Length; ++counter)
225             {
226                 string argument = args[counter];
227 
228                 if (argument != null)
229                 {
230                     bool breakUpParameter = false;
231                     bool notAfterDashA = false;
232 
233                     if (argument.Length > 2)
234                     {
235                         if (!rpxFound)
236                         {
237                             if (argument.ToLowerInvariant().EndsWith(".rpx") ||
238                                 argument.ToLowerInvariant().EndsWith(".elf"))
239                             {
240                                 rpxFound = true;
241                                 rpxIndex = counter;
242                             }
243                         }
244 
245                         if (counter < rpxIndex)
246                         {
247                             // Special Cases
248                             switch (argument.Substring(0, 2))
249                             {
250                                 case "-t":
251                                     notAfterDashA = (counter == 0) || (!(args[counter - 1].StartsWith("-a")));
252                                     if (notAfterDashA)
253                                     {
254                                         breakUpParameter = true;
255                                     }
256                                     break;
257                                 case "-w":
258                                     notAfterDashA = (counter == 0) || (!(args[counter - 1].StartsWith("-a")));
259                                     if (notAfterDashA)
260                                     {
261                                         breakUpParameter = true;
262                                     }
263                                     break;
264                                 default:
265                                     // Nothing to do : breakUpParameter = false
266                                     break;
267                             }
268                         }
269                     }
270 
271                     if (breakUpParameter)
272                     {
273                         // This is a case where the cafe argument and setting are not spaced out properly
274                         newList.Add(argument.Substring(0, 2));
275                         newList.Add(argument.Substring(2));
276                     }
277                     else
278                     {
279                         // Just add the argument - it is good!
280                         newList.Add(argument);
281                     }
282                 }
283             }
284 
285             return newList.ToArray();
286         }
287 
288         /// <summary>
289         /// Sets either the Console.In or Console.Out encoding using the encoder name or codepage provided.
290         /// </summary>
291         /// <param name="target">Console In or Console Out.</param>
292         /// <param name="encoderValue">String representing the codepage or encoding name.</param>
293         /// <returns>True if encoding was changed, false if not.</returns>
SetEncoding(TargetEncoding target, string encoderValue)294         static private bool SetEncoding(TargetEncoding target, string encoderValue)
295         {
296             if (string.IsNullOrEmpty(encoderValue))
297             {
298                 // It will use the default encoders.
299                 return false;
300             }
301 
302             int codePage = 0;
303             bool returnVal = false;
304             string defaultEncodingCodePage = Encoding.Default.WindowsCodePage.ToString();
305 
306             // could be a string as the name of the encoding
307             if (encoderValue.ToLowerInvariant().Equals("default"))
308             {
309                 encoderValue = defaultEncodingCodePage;
310             }
311 
312             //handle situation where user provides the number for the codepage
313             if (int.TryParse(encoderValue, out codePage))
314             {
315                 if (codePage > 0)
316                 {
317                     //It was given a valid codepage
318                     try
319                     {
320                         switch (target)
321                         {
322                             case TargetEncoding.ConsoleIn:
323                                 Console.InputEncoding = Encoding.GetEncoding(codePage);
324                                 returnVal = true;
325                                 break;
326                             case TargetEncoding.ConsoleOut:
327                                 Console.OutputEncoding = Encoding.GetEncoding(codePage);
328                                 returnVal = true;
329                                 break;
330                             case TargetEncoding.CafeOut:
331                                 Program.cafeOutEncoding = Encoding.GetEncoding(codePage);
332                                 returnVal = true;
333                                 break;
334                         }
335                     }
336                     catch (ArgumentOutOfRangeException)
337                     { }
338                     catch (ArgumentException)
339                     { }
340                     catch (NotSupportedException)
341                     { }
342                     catch (IOException)
343                     { }
344                     catch (System.Security.SecurityException)
345                     { }
346                 }
347             }
348             else //did they pass in the string representation of the encoding?
349             {
350                 try
351                 {
352                     switch (target)
353                     {
354                         case TargetEncoding.ConsoleIn:
355                             Console.InputEncoding = Encoding.GetEncoding(encoderValue);
356                             returnVal = true;
357                             break;
358                         case TargetEncoding.ConsoleOut:
359                             Console.OutputEncoding = Encoding.GetEncoding(encoderValue);
360                             returnVal = true;
361                             break;
362                         case TargetEncoding.CafeOut:
363                             Program.cafeOutEncoding = Encoding.GetEncoding(encoderValue);
364                             returnVal = true;
365                             break;
366                     }
367                 }
368                 catch (ArgumentException)
369                 { }
370                 catch (NotSupportedException)
371                 { }
372                 catch (IOException)
373                 { }
374                 catch (System.Security.SecurityException)
375                 { }
376             }
377 
378             if (!returnVal && !encoderValue.Equals(defaultEncodingCodePage))
379             {
380                 // Failed with the user request, trying to default
381                 Console.WriteLine("cafex : unable to set {0} encoding to {1}. Reverting to default {2}.", target.ToString(), encoderValue, defaultEncodingCodePage);
382                 return SetEncoding(target, defaultEncodingCodePage);
383             }
384 
385             return returnVal;
386         }
387 
388         private static HandlerRoutine cchr = null;
389 
ConsoleCtrlCheck(CtrlTypes ctrlType)390         private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
391         {
392             Console.WriteLine("\ncafex: terminated");
393             Environment.Exit(0);
394             return true;
395         }
396 
Main(string[] args)397         static int Main(string[] args)
398         {
399 #if TIMER
400             string logFilePath = "%CAFE_TEMP%\\%SESSION_PATH_PREFIX%caferun\\CafeXPerf.log";
401             Log.OpenLogFile(logFilePath);
402             CafeXSetupTime.Start();
403 #endif
404             cchr = new HandlerRoutine(ConsoleCtrlCheck);
405             SetConsoleCtrlHandler(cchr, true);
406             GC.KeepAlive(cchr);
407 
408 #if DEBUG
409             // System.Diagnostics.Debugger.Break();
410             string logFilePath = "%CAFE_TEMP%\\%SESSION_PATH_PREFIX%caferun\\CafeX.log";
411             Log.OpenLogFile(logFilePath);
412             Log.WriteLine("-------------------------- CAFEX NEW INSTANCE ----------------------------------");
413             Log.WriteLine("Main started. Command line = " + Environment.CommandLine);
414 #endif
415 
416             if(CAFE_PROFILE.value != null && CAFE_PROFILE.value == "1")
417             {
418                 Console.WriteLine("CafeX: Profiling is enabled!");
419                 CafeXEventtLog.Instance.WriteToEventLog(500, DateTime.Now, EventStatus.BEGIN, EventProcess.CAFEX, "CAFEX NEW INSTANCE");
420             }
421             StreamWriter output_writer = null;
422             TextWriter old_out = null;
423             TextWriter old_err = null;
424 
425             FileUtil.GatherEnvironment();
426 
427             // Setting default encoding variables
428             if (string.IsNullOrEmpty(CAFEX_STDIN_ENCODING.value))
429             {
430                 CAFEX_STDIN_ENCODING.value = "default";
431             }
432 
433             if (string.IsNullOrEmpty(CAFEX_STDOUT_ENCODING.value))
434             {
435                 // Standard out should be different depending on the country.
436                 int defaultEncodingCodePage = Encoding.Default.WindowsCodePage;
437                 switch(defaultEncodingCodePage)
438                 {
439                     case 932: //Japan - Shift_JIS
440                         CAFEX_STDOUT_ENCODING.value = "default";
441                         break;
442                     default:
443                     CAFEX_STDOUT_ENCODING.value = "utf-8";
444                         break;
445                 }
446             }
447 
448             try
449             {
450                 int arg_pos = 0;
451                 args = CleanupArguments(args);
452 
453                 // Handle the arguments to cafex in any order.
454                 // Valid options are -q and -o.
455                 while (true)
456                 {
457                     if (args.Length > arg_pos && args[arg_pos] == "-q")
458                     {
459                         quiet_mode = true;
460                         ++arg_pos;
461                     }
462                     // Format for -o is -o [filename], so need to make sure there are 2 more arguments.
463                     else if (args.Length > arg_pos + 1 && args[arg_pos] == "-o")
464                     {
465                         if (!ArgumentChecks.IsArgumentValidSetting(args, arg_pos + 1))
466                         {
467                             PrintCafeXHelp();
468                             return (int)CAFEX_ERROR.BAD_ARGUMENT;
469                         }
470 
471                         string output_file = PathConverter.Windowsify(args[arg_pos + 1]);
472                         if (!FileUtil.IsValidFile(output_file, false))
473                         {
474                             Console.WriteLine("Invalid file name!\n");
475                             PrintCafeXHelp();
476                             return (int) CAFEX_ERROR.BAD_ARGUMENT;
477                         }
478 
479                         arg_pos += 2;
480 
481                         FileStream fs = null;
482                         try
483                         {
484                             fs = new FileStream(output_file, FileMode.Create);
485                         }
486                         catch (System.UnauthorizedAccessException)
487                         {
488                             Console.WriteLine(PERMISSION_ERROR_TEXT);
489                             PrintCafeXHelp();
490                             return (int)CAFEX_ERROR.BAD_ARGUMENT;
491                         }
492                         catch (System.Security.SecurityException)
493                         {
494                             Console.WriteLine(PERMISSION_ERROR_TEXT);
495                             PrintCafeXHelp();
496                             return (int)CAFEX_ERROR.BAD_ARGUMENT;
497                         }
498 
499                         old_out = Console.Out;
500                         old_err = Console.Error;
501                         output_writer = new StreamWriter(fs);
502                         Console.SetOut(output_writer);
503                         Console.SetError(output_writer);
504                     }
505                     else if (args.Length > arg_pos + 1 && args[arg_pos] == "-I")
506                     {
507                         // Get the STDIN encoding
508                         if (!ArgumentChecks.IsArgumentValidSetting(args, arg_pos + 1))
509                         {
510                             PrintCafeXHelp();
511                             return (int)CAFEX_ERROR.BAD_ARGUMENT;
512                         }
513 
514                         CAFEX_STDIN_ENCODING.value = args[arg_pos + 1];
515                         arg_pos += 2;
516                     }
517                     else if (args.Length > arg_pos + 1 && args[arg_pos] == "-O")
518                     {
519                         if (!ArgumentChecks.IsArgumentValidSetting(args, arg_pos + 1))
520                         {
521                             PrintCafeXHelp();
522                             return (int)CAFEX_ERROR.BAD_ARGUMENT;
523                         }
524 
525                         CAFEX_STDOUT_ENCODING.value = args[arg_pos + 1];
526                         arg_pos += 2;
527                     }
528                     else if (args[arg_pos] == "-noinput")
529                     {
530                         no_console = true;
531                         arg_pos++;
532                     }
533                     else
534                     {
535                         // If it's anything else then it's the command.
536                         break;
537                     }
538                 }
539 
540 
541                 // Get the command.
542                 if (args.Length > arg_pos)
543                 {
544                     command = args[arg_pos].ToLowerInvariant();
545                     ++arg_pos;
546                 }
547 
548                 CAFEX_ERROR ret = InitialEnvVarSetup();
549                 if (ret != CAFEX_ERROR.OK)
550                 {
551                     return (int)ret;
552                 }
553 
554                 ret = CheckCafeRootAndHostBridgeVars();
555                 if (ret != CAFEX_ERROR.OK)
556                 {
557                     return (int)ret;
558                 }
559 
560                 if (!string.IsNullOrEmpty(NO_CONSOLE.value) && NO_CONSOLE.value == "1")
561                 {
562                     // Disable console input if it was specified in the enviromnment
563                     no_console = true;
564                 }
565 
566                 // Sets the quiet mode if CAFE_CONSOLE is toucan and
567                 // "-q" parameter was not provided by caller.
568                 if (!quiet_mode && (CAFE_CONSOLE.value != "cattoucan"))
569                 {
570                     quiet_mode = true;
571                 }
572                 else if (CAFE_CONSOLE.value == "cattoucan")
573                 {
574                     // If explicitly setting console to cattoucan, it
575                     // should override even if the "-q" parameter was
576                     // set.  This solves validation.sh tests running
577                     // in softlaunch mode.
578                     quiet_mode = false;
579                 }
580 
581                 // Print the encoding
582                 if (!command.Equals("stop"))
583                 {
584                     // Set encodings
585                     if (CafeX.Program.IsInputRedirected)
586                     {
587                         Console.WriteLine("cafex : input is redirected. Using legacy keyboard input and not changing encodings.");
588                     }
589                     else
590                     {
591                         SetEncoding(TargetEncoding.ConsoleIn, CAFEX_STDIN_ENCODING.value);
592                         SetEncoding(TargetEncoding.ConsoleOut, CAFEX_STDOUT_ENCODING.value);
593 
594                         Console.WriteLine("cafex input/keyboard encoding code page: {0}", Console.InputEncoding.CodePage);
595                         Console.WriteLine("cafex output/screen  encoding code page: {0}", Console.OutputEncoding.CodePage);
596                     }
597 
598                     //always want to set this.
599                     SetEncoding(TargetEncoding.CafeOut, CAFE_OUTPUT_ENCODING.value);
600                     if (Program.cafeOutEncoding != null)
601                     {
602                         Console.WriteLine("cafe output/stream   encoding code page: {0}", Program.cafeOutEncoding.CodePage);
603                     }
604                 }
605 
606                 int args_to_copy = args.Length - arg_pos;
607                 string[] args_for_command = new string[args_to_copy];
608                 Array.Copy(args, arg_pos, args_for_command, 0, args_to_copy);
609 
610                 CafeXReturn exitCode = DoCommand(args_for_command, quiet_mode);
611                 if (exitCode.error == CAFEX_ERROR.BAD_ARGUMENT)
612                 {
613                     Console.WriteLine("cafex : Bad command.");
614                     PrintCafeXHelp();
615                 }
616 
617                 if (exitCode.error != CAFEX_ERROR.OK)
618                     return (int)exitCode.error;
619                 else
620                     return exitCode.appExitCode;
621             }
622             catch (Exception e)
623             {
624                 Console.OutputEncoding.GetEncoder().Reset();
625                 string errorFile = "cafexErrorLog_" + System.Diagnostics.Process.GetCurrentProcess().Id.ToString() + "_" +
626                                    DateTime.Now.ToString("yyyyMMddHHmm") + ".log";
627                 string errorLog = "CafeX error log\nDate:" + DateTime.Now + "\nError message:" +
628                                    e.Message + "\nStack trace:" + e.StackTrace + "\n";
629                 if (string.IsNullOrEmpty(CAFE_TEMP.value))
630                 {
631                     errorFile = Path.Combine(Directory.GetCurrentDirectory(), SESSION_PATH_PREFIX.value + errorFile);
632                 }
633                 else
634                 {
635                     errorFile = Path.Combine(CAFE_TEMP.value, SESSION_PATH_PREFIX.value + errorFile);
636                 }
637 
638                 try
639                 {
640                     System.IO.File.WriteAllText(errorFile, errorLog);
641                     Console.WriteLine("CafeX error log was saved as '{0}'", errorFile);
642                 }
643                 catch (IOException)
644                 {
645                     Console.WriteLine("Unable to write the file '{0}'", errorFile);
646                     Console.WriteLine("Writing log to console window:");
647                     Console.WriteLine(errorLog);
648                 }
649 
650                 Console.WriteLine("CafeX encountered an error executing the command.");
651                 Console.WriteLine("Please confirm this is a supported command and has the correct arguments.");
652                 Console.WriteLine("Error message: " + e.Message);
653 
654                 return (int)CAFEX_ERROR.UNEXPECTED;
655             }
656             finally
657             {
658                 // Re-set the original stdout and stderr.
659                 if (output_writer != null)
660                 {
661                     Console.SetOut(old_out);
662                     Console.SetError(old_err);
663                     output_writer.Close();
664                 }
665 #if DEBUG || TIMER
666                 Log.WriteLine("-------------------------- END OF CAFEX INSTANCE ----------------------------------");
667                 Log.CloseLogFile();
668 #endif
669                 if (CAFE_PROFILE.value != null && CAFE_PROFILE.value == "1")
670                 {
671                     CafeXEventtLog.Instance.WriteToEventLog(501, DateTime.Now, EventStatus.END, EventProcess.CAFEX, "END OF CAFEX INSTANCE");
672                 }
673 
674             }
675 
676         }
677 
678 
CheckCafeRootAndHostBridgeVars()679         internal static CAFEX_ERROR CheckCafeRootAndHostBridgeVars()
680         {
681 #if DEBUG
682             Log.WriteLine("CheckCafeRootAndHostBridgeVars started.");
683 #endif
684 
685             CAFEX_ERROR ret = CAFEX_ERROR.OK;
686 
687             if (CAFE_ROOT.value == null)
688             {
689                 Console.WriteLine("CafeX Error: CAFE_ROOT environment variable is not set.");
690                 ret = CAFEX_ERROR.NO_SDK_HOSTBRIDGE_ENV_VARS;
691             }
692             if (MION_BRIDGE_TOOLS.value == null)
693             {
694                 Console.WriteLine("CafeX Error: MION_BRIDGE_TOOLS environment variable is not set.");
695                 ret = CAFEX_ERROR.NO_SDK_HOSTBRIDGE_ENV_VARS;
696             }
697 
698             return ret;
699         }
700 
CheckDevkitVars()701         internal static CAFEX_ERROR CheckDevkitVars()
702         {
703 #if DEBUG
704             Log.WriteLine("CheckDevkitVars started. Retuns CAFEX_ERRO.OK if BRIDGE_CURRENT_NAME and BRIDGE_CURRENT_IP_ADDRESS are provided.");
705 #endif
706 
707             CAFEX_ERROR ret = CAFEX_ERROR.OK;
708 
709             if (string.IsNullOrEmpty(BRIDGE_CURRENT_NAME.value))
710             {
711                 Console.WriteLine("CafeX Error: BRIDGE_CURRENT_NAME environment variable is not set.");
712                 ret = CAFEX_ERROR.NO_DEVKIT_VARS;
713             }
714             if (string.IsNullOrEmpty(BRIDGE_CURRENT_IP_ADDRESS.value))
715             {
716                 Console.WriteLine("CafeX Error: BRIDGE_CURRENT_IP_ADDRESS environment variable is not set.");
717                 ret = CAFEX_ERROR.NO_DEVKIT_VARS;
718             }
719 
720             return ret;
721         }
722 
DoCommand(string[] args, bool quiet_mode)723         internal static CafeXReturn DoCommand(string[] args, bool quiet_mode)
724         {
725 #if DEBUG
726             Log.WriteLine("DoCommand started. quiet_mode=" + quiet_mode.ToString());
727             string argString = null;
728             if (args != null)
729             {
730                 foreach (string arg in args)
731                 {
732                     argString += arg + " ";
733                 }
734             }
735             Log.WriteLine("Arguments=" + argString);
736 #endif
737 
738             CafeXReturn ret = new CafeXReturn(CAFEX_ERROR.OK);
739 
740             switch (command.ToLowerInvariant())
741             {
742                 case "help":
743                     {
744                         PrintCafeXHelp();
745                         break;
746                     }
747 
748                 case "stop":
749                     {
750                         ret.error = CheckDevkitVars();
751                         if (ret.error != CAFEX_ERROR.OK)
752                             break;
753 
754                         stop(args);
755 
756                         break;
757                     }
758 
759                 case "run":
760                     {
761                         ret.error = CheckDevkitVars();
762                         if (ret.error != CAFEX_ERROR.OK)
763                             break;
764 
765                         ret = run(args, true, true);
766 
767                         break;
768                     }
769 
770                 case "discrun":
771                     {
772                         ret.error = CheckDevkitVars();
773                         if (ret.error != CAFEX_ERROR.OK)
774                             break;
775 
776                         ret = discrun(args, true, true);
777 
778                         break;
779                     }
780 
781                 case "headlessrun":
782                     {
783                         ret.error = CheckDevkitVars();
784                         if (ret.error != CAFEX_ERROR.OK)
785                             break;
786 
787                         ret = headlessrun(args);
788                         break;
789                     }
790 
791                 case "on":
792                     {
793                         ret.error = CheckDevkitVars();
794                         if (ret.error != CAFEX_ERROR.OK)
795                             break;
796 
797                         ret = on(args, true, true);
798 
799                         break;
800                     }
801 
802                 case "update":
803                     {
804                         ret.error = CheckDevkitVars();
805                         if (ret.error != CAFEX_ERROR.OK)
806                             break;
807 
808                         ret.error = update(args);
809 
810                         break;
811                     }
812 
813                 case "revert":
814                     {
815                         ret.error = CheckDevkitVars();
816                         if (ret.error != CAFEX_ERROR.OK)
817                             break;
818 
819                         ret.error = revert(args);
820 
821                         break;
822                     }
823 
824                 case "recover":
825                     {
826                         ret.error = CheckDevkitVars();
827                         if (ret.error != CAFEX_ERROR.OK)
828                             break;
829 
830                         ret.error = recover(args);
831 
832                         break;
833                     }
834 
835                 case "cleardata":
836                     {
837                         ret.error = CheckDevkitVars();
838                         if (ret.error != CAFEX_ERROR.OK)
839                             break;
840 
841                         ret.error = cleardata(args);
842 
843                         break;
844                     }
845 
846                 case "setbootmode":
847                     {
848                         ret.error = CheckDevkitVars();
849                         if (ret.error != CAFEX_ERROR.OK)
850                             break;
851 
852                         ret.error = setbootmode(args);
853 
854                         break;
855                     }
856 
857                  case "syncsession":
858                     {
859                         ret.error = CheckDevkitVars();
860                         if (ret.error != CAFEX_ERROR.OK)
861                             break;
862 
863                         ret.error = syncsession(args);
864 
865                         break;
866                     }
867                 case "makeaoc":
868                 case "makemaster":
869                 case "makedisc":
870                 case "makedownload":
871                     {
872                         Console.WriteLine("CafeX Error: This function is no longer supported. Please use makecfmaster.exe to use commands for mastering.");
873                         ret.error = CAFEX_ERROR.BAD_COMMAND;
874                         break;
875                     }
876                 case "findbridges":
877                     {
878                         break;
879                     }
880 
881                 case "version":
882                     {
883                         System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
884 
885                         // With an AssemblyVersion of <major>.<minor>.*, the build is the number of days since 1/1/2000
886                         //   and the revision is the number of seconds since midnight
887                         Version asmVer = asm.GetName().Version;
888 
889                         DateTime buildDate = new DateTime(2000, 1, 1);           // Start at 1/1/2000
890                         buildDate = buildDate.AddDays(asmVer.Build);            // Add the days since 1/1/2000
891                         buildDate = buildDate.AddSeconds(asmVer.Revision * 2);  // Add the number of seconds since midnight
892 
893                         System.Diagnostics.FileVersionInfo fileVer = System.Diagnostics.FileVersionInfo.GetVersionInfo(asm.Location);
894 
895                         //NEVER EVER CHANGE THE NEXT TWO LINES.  YOUR LIFE MAY BE FORFEIT IF YOU DO.
896                         Console.WriteLine("cafex {0}", fileVer.FileVersion);
897                         Console.WriteLine("{0}",buildDate.ToString("MM/dd/yyyy hh:mm tt",DateTimeFormatInfo.InvariantInfo));
898 
899                         ret.error = CAFEX_ERROR.OK;
900                         break;
901                     }
902                 case "launch":
903                     {
904                         ret.error = launchTitle(args);
905                         break;
906                     }
907                 default:
908                     {
909                         if (string.IsNullOrEmpty(command) || command.StartsWith("-"))
910                         {
911                             Console.WriteLine("CafeX Error: Missing command. Please try \"cafex help\" for usage information.");
912                         }
913                         else
914                         {
915                             Console.WriteLine("CafeX Error: Invalid command '{0}'. Please try \"cafex help\" for usage information.", command);
916                         }
917                         ret.error = CAFEX_ERROR.BAD_COMMAND;
918                         break;
919                     }
920             }
921 
922             return ret;
923         }
924 
925         private enum FileType { Unknown, Disk, Char, Pipe };
926         private enum StdHandle { Stdin = -10, Stdout = -11, Stderr = -12 };
927         [DllImport("kernel32.dll")]
GetFileType(IntPtr hdl)928         private static extern FileType GetFileType(IntPtr hdl);
929         [DllImport("kernel32.dll")]
GetStdHandle(StdHandle std)930         private static extern IntPtr GetStdHandle(StdHandle std);
931         [DllImport("Kernel32")]
SetConsoleCtrlHandler(HandlerRoutine Handler, Boolean Add)932         public static extern Boolean SetConsoleCtrlHandler(HandlerRoutine Handler,
933             Boolean Add);
934 
935         // sent to the handler routine.
936         public enum CtrlTypes
937         {
938 
939             CTRL_C_EVENT = 0,
940             CTRL_BREAK_EVENT,
941             CTRL_CLOSE_EVENT,
942             CTRL_LOGOFF_EVENT = 5,
943             CTRL_SHUTDOWN_EVENT
944         }
945 
946 
947         // A delegate type to be used as the handler routine
948         // for SetConsoleCtrlHandler.
HandlerRoutine(CtrlTypes CtrlType)949         public delegate bool HandlerRoutine(CtrlTypes CtrlType);
950 
951         internal static bool IsOutputRedirected
952         {
953             get
954             {
955                 return FileType.Char != GetFileType(GetStdHandle(StdHandle.Stdout));
956             }
957         }
958 
959         internal static bool IsInputRedirected
960         {
961             get
962             {
963                 return FileType.Char != GetFileType(GetStdHandle(StdHandle.Stdin));
964             }
965         }
966 
967 
PrintCafeXHelp()968         internal static void PrintCafeXHelp()
969         {
970             Console.WriteLine("Usage: cafex [options] [command] [arguments]");
971             Console.WriteLine();
972             Console.WriteLine("options:");
973             Console.WriteLine(" -q              The command will be executed, and CafeX returns immediately. No output is collected.");
974             Console.WriteLine(" -o <filename>   All output is redirected to the specified file.");
975             Console.WriteLine(" -I <encoding>   The encoding for the console standard in. Valid encoding arguments are: codepage");
976             Console.WriteLine("                 (eg. 65001, 932, etc), or the encoding name (eg. UTF-8, shift_jis, etc).");
977             Console.WriteLine("                 This setting overrides the CAFEX_STDIN_ENCODING environment variable.");
978             Console.WriteLine("                 Note: If the environment variable and this switch is not provided, the default windows encoding is used.");
979             Console.WriteLine(" -O <encoding>   The encoding for the console standard out. Valid encoding arguments are: codepage");
980             Console.WriteLine("                 (eg. 65001, 932, etc), or the encoding name (eg. UTF-8, shift_jis, etc).");
981             Console.WriteLine("                 This setting overrides the CAFEX_STDOUT_ENCODING environment variable.");
982             Console.WriteLine("                 Note: If the environment variable and this switch is not provided, UTF-8 is default.");
983             Console.WriteLine(" -noinput        Disables reading of the PC keyboard or STDIN if redirected.");
984             Console.WriteLine("                 Note: Setting the environment variable NO_CONSOLE=1 has the same effect.");
985             Console.WriteLine();
986             Console.WriteLine("commands:");
987             Console.WriteLine(" stop            Stop the devkit.");
988             Console.WriteLine(" run             Run an application.");
989             Console.WriteLine(" discrun         Run an application using block level interface.");
990             Console.WriteLine(" headlessrun     Run an application disconnected from the network.");
991             Console.WriteLine(" on              Start System Config Tool.");
992             Console.WriteLine(" update          Update devkit firmware.");
993             Console.WriteLine(" revert          Downgrade devkit OS to an older SDK.");
994             Console.WriteLine(" recover         Attempt to recover a devkit from corrupted OS.");
995             Console.WriteLine(" cleardata       Clear the user data in NAND.");
996             Console.WriteLine(" setbootmode     Change the boot mode of the devkit.");
997             Console.WriteLine(" syncsession     Sync the session data directory with the default one.");
998             Console.WriteLine(" launch          Launches a title through system config tool");
999             Console.WriteLine();
1000             Console.WriteLine(" version         Prints version and build information then exits.");
1001             Console.WriteLine();
1002             Console.WriteLine("arguments:");
1003             Console.WriteLine("Execute cafex [command] -h for information on valid arguments for each command.");
1004         }
1005     }
1006 }
1007