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