/*---------------------------------------------------------------------------*
Copyright 2010-2012 Nintendo. All rights reserved.
These coded instructions, statements, and computer programs contain
proprietary information of Nintendo of America Inc. and/or Nintendo
Company Ltd., and are protected by Federal copyright law. They may
not be disclosed to third parties or copied or duplicated in any form,
in whole or in part, without the prior written consent of Nintendo.
*---------------------------------------------------------------------------*/
using System;
using System.Diagnostics;
using System.Net.Sockets;
using System.IO;
using System.Net;
using System.Collections.Generic;
using System.Threading;
using System.Text;
namespace CafeX
{
///
/// This class encapsulates the functionality of the dkt.exe tool used by CafeX.
///
static class dkt
{
static string cygwin_dkt_exe_path = "\"" + Environment.GetEnvironmentVariable("CAFE_ROOT") + "\\system\\bin\\tool\\dkt.exe" + "\"";
private static string GetExeName()
{
string dkt_or_temp_exe_path;
if (FileUtil.RunningFromCygwin)
{
// take the monitor from cygwin
dkt_or_temp_exe_path = cygwin_dkt_exe_path;
}
else
{
dkt_or_temp_exe_path = FileUtil.ExpandExecutable(CafeX.Properties.Resources.win_dkt, "win_dkt.exe");
}
return dkt_or_temp_exe_path;
}
internal static int FlushOutputBuffer()
{
#if DEBUG
Log.WriteLine("dkt.FlushOutputBuffer started -> calls FlushOuputBuffer( 1 second).");
#endif
return dkt.FlushOutputBuffer(1);
}
internal static int FlushOutputBuffer(uint seconds)
{
string strSeconds = seconds.ToString();
#if DEBUG
Log.WriteLine("dkt.FlushOutputBuffer(" + strSeconds + ") started.");
#endif
return ProcessExecutor.DoProcess(GetExeName(), "-d 40 " + strSeconds);
}
}
///
/// This class encapsulates the functionality of the PCFSServer used by CafeX.
///
static class PCFSServer
{
static string exe_path = null;
static string output_log = String.Empty;
private static void setExePath()
{
string testInMionBridge = Environment.GetEnvironmentVariable("MION_BRIDGE_TOOLS") + "\\pcfs\\PCFSServer.exe";
if (File.Exists(testInMionBridge))
{
exe_path = testInMionBridge;
}
else
{
exe_path = Environment.GetEnvironmentVariable("CAFE_ROOT") + "\\system\\bin\\tool\\PCFSServer.exe";
}
FileUtil.AddQuotes(ref exe_path);
}
internal static int Shutdown()
{
#if DEBUG
Log.WriteLine("PCFSServer.Shutdown started.");
#endif
setExePath();
return ProcessExecutor.DoProcess(exe_path, "-q");
}
internal static int Start(string parameters, string mapping, string log)
{
#if DEBUG
Log.WriteLine("PCFSServer.Start started. parameters=" + parameters + ", mapping=" + mapping + ", log=" + log);
#endif
setExePath();
output_log = log;
return ProcessExecutor.StartProcess_RedirectOutput(exe_path, "-r 100 " + parameters + " " + Environment.GetEnvironmentVariable("PCFS_HEADLESS_EMUL") + " " + mapping, output_callback, false);
}
static void output_callback(Object sender, DataReceivedEventArgs line)
{
#if DEBUG
Log.WriteLine("PCFSServer.output_callback started.");
#endif
setExePath();
if (output_log != "/dev/null")
{
try
{
FileStream s = new FileStream(output_log, FileMode.Append, FileAccess.Write);
StreamWriter w = new StreamWriter(s);
w.Write(line.Data);
w.Flush();
w.Close();
}
catch (IOException ex)
{
Console.WriteLine("cafex [PCFSServer]: Caught exception while writing stdout to '{0}':", output_log);
Console.WriteLine(" {0}", ex.Message);
// Prevent future log file writes
output_log = "/dev/null";
}
}
}
}
///
/// This class encapsulates the functionality of the checkbridge tool used by CafeX.
///
static class checkbridgename
{
static string exe_path = "\"" + Environment.GetEnvironmentVariable("MION_BRIDGE_TOOLS") + "\\checkbridgename.exe" + "\"";
internal static int CheckName(ref string output)
{
#if DEBUG
Log.WriteLine("checkbridgename.CheckName started.");
#endif
string args = string.Format("\"{0}\" -ip {1}",
Environment.GetEnvironmentVariable("BRIDGE_CURRENT_NAME"),
Environment.GetEnvironmentVariable("BRIDGE_CURRENT_IP_ADDRESS"));
int returnVal = ProcessExecutor.DoProcess_GetOutput(exe_path, args, out output);
return returnVal;
}
internal static int CheckName()
{
string output = string.Empty;
return CheckName(ref output);
}
internal static int CheckName_IPOnly()
{
#if DEBUG
Log.WriteLine("checkbridgename.CheckName_IPOnly started.");
#endif
int returnVal;
string output = string.Empty;
returnVal = ProcessExecutor.DoProcess_GetOutput(exe_path, "-s -ip " + Environment.GetEnvironmentVariable("BRIDGE_CURRENT_IP_ADDRESS"), out output);
Console.Write(output);
return returnVal;
}
}
///
/// This class encapsulates the functionality of the toucanreset tool used by CafeX.
///
static class ToucanReset
{
static string exe_path = "\"" + Environment.GetEnvironmentVariable("MION_BRIDGE_TOOLS") + "\\ToucanReset.exe" + "\"";
internal static int Stop()
{
#if DEBUG
Log.WriteLine("ToucanReset.Stop started.");
#endif
return ProcessExecutor.DoProcess(exe_path, "-stop");
}
internal static int Reset()
{
#if DEBUG
Log.WriteLine("ToucanReset.Reset started.");
#endif
return ProcessExecutor.DoProcess(exe_path, Environment.GetEnvironmentVariable("HOSTSTOP_RESET_OPTION"));
}
}
///
/// This class encapsulates the functionality of the fsemul tool used by CafeX.
///
static class FSEmul
{
static string exe_path = "\"" + Environment.GetEnvironmentVariable("MION_BRIDGE_TOOLS") + "\\FSEmul.exe" + "\"";
internal static int makemine()
{
#if DEBUG
Log.WriteLine("FSEmul.makemine started.");
#endif
int returnVal;
string output = string.Empty;
returnVal = ProcessExecutor.DoProcess_GetOutput(exe_path, "-ip " + Environment.GetEnvironmentVariable("BRIDGE_CURRENT_IP_ADDRESS") + " -makemine", out output);
Console.Write(output);
return returnVal;
}
internal static int FW_SW_Version(string ip, out string fw_ver, out string sw_ver)
{
#if DEBUG
Log.WriteLine("FSEmul.FW_SW_Version started.");
#endif
string arguments = (ip != null) ? "-ver -ip " + ip : "-ver";
// Remember and clear the TOUCAN_VERBOSE variable as this causes the version to be parsed as blank
string localTOUCAN_VERBOSE = Environment.GetEnvironmentVariable("TOUCAN_VERBOSE");
Environment.SetEnvironmentVariable("TOUCAN_VERBOSE", "");
string output = string.Empty;
int ret = ProcessExecutor.DoProcess_GetOutput(exe_path, arguments, out output);
// Restore the TOUCAN_VERBOSE variable
if (!string.IsNullOrEmpty(localTOUCAN_VERBOSE))
{
Environment.SetEnvironmentVariable("TOUCAN_VERBOSE", localTOUCAN_VERBOSE);
}
if (ret != 0)
{
Console.WriteLine("FSEmul failed: Couldn't get firmware versions");
fw_ver = string.Empty;
sw_ver = string.Empty;
return ret;
}
char[] delimiters = new char[3];
delimiters[0] = ' ';
delimiters[1] = '\r';
delimiters[2] = '\n';
string[] split = output.Split(delimiters);
fw_ver = split[3];
sw_ver = split[8];
return ret;
}
internal static int boot_mode_detect(string ip, out string boot_mode)
{
#if DEBUG
Log.WriteLine("FSEmul.boot_mode_detect started.");
#endif
if (string.IsNullOrEmpty(ip))
{
boot_mode = "UNKNOWN";
return 1;
}
string arguments = "-ip " + ip + " -modedetect";
// Remember and clear the TOUCAN_VERBOSE variable as this causes the version to be parsed as blank
string localTOUCAN_VERBOSE = Environment.GetEnvironmentVariable("TOUCAN_VERBOSE");
Environment.SetEnvironmentVariable("TOUCAN_VERBOSE", "");
string output = string.Empty;
int ret = ProcessExecutor.DoProcess_GetOutput(exe_path, arguments, out output);
// Restore the TOUCAN_VERBOSE variable
if (!string.IsNullOrEmpty(localTOUCAN_VERBOSE))
{
Environment.SetEnvironmentVariable("TOUCAN_VERBOSE", localTOUCAN_VERBOSE);
}
if (ret != 0)
{
Console.WriteLine("FSEmul failed: Couldn't detect boot mode!");
boot_mode = "UNKNOWN";
return ret;
}
boot_mode = output.Trim();
return 0;
}
internal static int start(string arguments, string fsemul_params)
{
#if DEBUG
Log.WriteLine("FSEmul.start started.");
#endif
DataReceivedEventHandler handler = new DataReceivedEventHandler((sender, args) => {
if (args != null && args.Data != null)
{
Console.WriteLine(args.Data);
// If FSEmul throws, suggest using cafestop -makemine
if (args.Data.ToString().Contains("Bridge is being used by the PC with IP Address"))
{
Console.WriteLine("\nTry using 'cafex stop -makemine' to take control of the Bridge.");
}
}
});
return ProcessExecutor.StartProcess_RedirectOutput(exe_path, arguments + " " + fsemul_params, handler, false);
}
}
///
/// This class encapsulates the functionality of the mionbutton tool used by CafeX.
///
static class MionButton
{
static string exe_path = "\"" + Environment.GetEnvironmentVariable("MION_BRIDGE_TOOLS") + "\\MionButton.exe" + "\"";
internal static int rsthold(string[] args)
{
#if DEBUG
Log.WriteLine("MionButton.rsthold started.");
#endif
string output = string.Empty;
int returnVal;
string arg_string = StringCombiner.MakeDelimitedString(args, ' ');
returnVal = ProcessExecutor.DoProcess_GetOutput(exe_path, "-ip " + Environment.GetEnvironmentVariable("BRIDGE_CURRENT_IP_ADDRESS") + " -rsthold " + arg_string, out output);
// Print output to console
Console.Write(output);
return returnVal;
}
internal static int forceOff1(string[] args)
{
#if DEBUG
Log.WriteLine("mionButton.forceOff1 started.");
#endif
string output = string.Empty;
int returnVal;
string arg_string = StringCombiner.MakeDelimitedString(args, ' ');
returnVal = ProcessExecutor.DoProcess_GetOutput(exe_path, "-ip " + Environment.GetEnvironmentVariable("BRIDGE_CURRENT_IP_ADDRESS") + " -forceOff1 " + arg_string, out output);
// Print output to console
Console.Write(output);
return returnVal;
}
internal static int init_shutdown_w_FOFF2(string[] args)
{
#if DEBUG
Log.WriteLine("MionButton.init_shutdown_w_FOFF2 started.");
#endif
string output = string.Empty;
int returnVal;
string arg_string = StringCombiner.MakeDelimitedString(args, ' ');
returnVal = ProcessExecutor.DoProcess_GetOutput(exe_path, "-ip " + Environment.GetEnvironmentVariable("BRIDGE_CURRENT_IP_ADDRESS") + " -init_shutdown_w_FOFF2 60000 " + arg_string, out output);
// Print output to console
Console.Write(output);
return returnVal;
}
internal static int forceOff2_off_single_cgi(string[] args)
{
#if DEBUG
Log.WriteLine("MionButton.forceOff2_off_single_cgi started.");
#endif
string output = string.Empty;
int returnVal;
string arg_string = StringCombiner.MakeDelimitedString(args, ' ');
returnVal = ProcessExecutor.DoProcess_GetOutput(exe_path, "-ip " + Environment.GetEnvironmentVariable("BRIDGE_CURRENT_IP_ADDRESS") + " -forceOff2_off_single_cgi " + arg_string, out output);
// Print output to console
Console.Write(output);
return returnVal;
}
}
///
/// This class encapsulates the functionality of the miontelnet tool used by CafeX.
///
static class miontelnet
{
static string exe_path = "\"" + Environment.GetEnvironmentVariable("CAFE_ROOT") + "\\system\\bin\\tool\\miontelnet.exe" + "\"";
internal static int Reboot(out string output)
{
#if DEBUG
Log.WriteLine("miontelnet.Reboot started.");
#endif
return ProcessExecutor.DoProcess_GetOutput(exe_path, "-reboot", out output);
}
internal static int Run(out string output)
{
#if DEBUG
Log.WriteLine("miontelnet.Run started.");
#endif
return ProcessExecutor.DoProcess_GetOutput(exe_path, null, out output);
}
}
///
/// This class encapsulates the functionality of the mionps tool used by CafeX.
///
static class mionps
{
static string exe_path = "\"" + Environment.GetEnvironmentVariable("CAFE_ROOT") + "\\system\\bin\\tool\\mionps.exe" + "\"";
private static readonly int MIONPS_TIMEOUT_MS = 30000; // 30 seconds
internal static string Run(string arguments)
{
#if DEBUG
Log.WriteLine("mionps.Run started.");
#endif
string output = string.Empty;
int returnVal = ProcessExecutor.DoProcess_GetOutput(exe_path, arguments, true, MIONPS_TIMEOUT_MS, out output);
// Currently, all the calls to mionps are assuming a single line without CRLF.
// If that changes, removing CRLF below will need to be removed and calls to RUN revisited.
output = output.Replace("\r\n", "");
// Error checking for mionps
if (returnVal != 0)
{
Console.WriteLine("cafex : Warning! mionps tool returned code {0}.", returnVal);
Console.WriteLine("mionps stdout and stderr received:");
Console.WriteLine(output);
}
return output;
}
}
///
/// This class encapsulates the functionality of the mionurl tool used by CafeX.
///
static class mionurl
{
static string exe_path = "\"" + Environment.GetEnvironmentVariable("CAFE_ROOT") + "\\system\\bin\\tool\\mionurl.exe" + "\"";
private static readonly int MIONURL_TIMEOUT_MS = 30000; // 30 seconds
internal static int Run(string arguments)
{
#if DEBUG
Log.WriteLine("mionurl.Run started.");
#endif
string output = string.Empty;
int returnVal = ProcessExecutor.DoProcess_GetOutput(exe_path, arguments, true, MIONURL_TIMEOUT_MS, out output);
// Currently, all the calls to mionurl are assuming a single line without CRLF.
// If that changes, removing CRLF below will need to be removed and calls to RUN revisited.
output = output.Replace("\r\n", "");
// Error checking for mionurl
if (returnVal != 0)
{
Console.WriteLine("cafex : Warning! mionurl tool returned code {0}.", returnVal);
Console.WriteLine("mionurl stdout and stderr received:");
Console.WriteLine(output);
}
return returnVal;
}
}
///
/// This class encapsulates the functionality of the sessionmanager tool used by CafeX.
///
static class SessionManagerUtil
{
static string exe_path = "\"" + Environment.GetEnvironmentVariable("MION_BRIDGE_TOOLS") + "\\SessionManagerUtil.exe" + "\"";
internal static int GetSessionInfo(out string output)
{
#if DEBUG
Log.WriteLine("SessionManagerUtil.GetSessionInfo started.");
#endif
return ProcessExecutor.DoProcess_GetOutput(exe_path, "-p NAME DEBUG_OUT DEBUG_CONTROL HIO_OUT LAUNCH_CTRL NET_MANAGE PCFS_SATA", out output);
}
}
///
/// This class encapsulates the functionality of the devkitmsg tool used by CafeX.
///
static class devkitmsg
{
internal static string CAFEX_RECOVER_TEST = Environment.GetEnvironmentVariable("CAFEX_RECOVER_TEST");
static string exe_path = "\"" + Environment.GetEnvironmentVariable("MION_BRIDGE_TOOLS") + "\\devkitmsg.exe" + "\"";
internal static int help(int timeout)
{
#if DEBUG
Log.WriteLine("devkitmsg.help started with timeout = " + timeout.ToString());
#endif
int returnVal = 2;
Console.WriteLine("cafex: Probing if COS is ready by sending devkit help msg:");
string msg = "\"help\" -v -p " + Environment.GetEnvironmentVariable("SESSION_LAUNCH_CTRL_PORT");
DateTime maxTime = DateTime.Now.AddMilliseconds((double)timeout);
do
{
returnVal = ProcessExecutor.DoProcess(exe_path, msg);
if (returnVal == 0)
{
break;
}
System.Threading.Thread.Sleep(Program.RETRY_TIMESPAN); // wait for a second.
}
while (maxTime.CompareTo(DateTime.Now) > 0);
#if DEBUG
Log.WriteLine("devkitmsg.help returnVal = " + returnVal.ToString());
#endif
return returnVal;
}
internal static int CosFlags(string ppc_os_flags)
{
#if DEBUG
Log.WriteLine("devkitmsg.CosFlags started.");
#endif
return ProcessExecutor.DoProcess(exe_path, "\"cos_flags " + ppc_os_flags + "\" -v -p " + Environment.GetEnvironmentVariable("SESSION_LAUNCH_CTRL_PORT"));
}
internal static int title_softlaunch_with_hint(string os, string tid, string hint, string port)
{
#if DEBUG
Log.WriteLine("devkitmsg.title_softlaunch_with_hint started.");
#endif
return ProcessExecutor.DoProcess(exe_path, "\"title_softlaunch " + os + " " + tid + " " + hint + "\" -v -p " + port);
}
internal static int title_softlaunch(string os, string tid, string port)
{
#if DEBUG
Log.WriteLine("devkitmsg.title_softlaunch started.");
#endif
DataReceivedEventHandler callback = (sender, e) =>
{
if (e != null && e.Data != null)
{
Console.WriteLine(e.Data);
}
};
return ProcessExecutor.StartProcess_RedirectOutput(exe_path, "\"title_softlaunch " + os + " " + tid + "\" -v -p " + port, callback, true);
}
internal static int recover()
{
#if DEBUG
Log.WriteLine("devkitmsg.recover started.");
#endif
if (CAFEX_RECOVER_TEST == "1")
{
Console.WriteLine("devkitmsg_cmd: recover -v -p ");
return 0;
}
return ProcessExecutor.DoProcess(exe_path, "recover -v -p " + Environment.GetEnvironmentVariable("SESSION_LAUNCH_CTRL_PORT"));
}
internal static int update_pcfs()
{
#if DEBUG
Log.WriteLine("dvkitmsg.update_pcfs started.");
#endif
if (CAFEX_RECOVER_TEST == "1")
{
Console.WriteLine("devkitmsg_cmd: update /vol/storage_hfiomlc01/sys/update/pcfs -v -p ");
return 0;
}
return ProcessExecutor.DoProcess(exe_path, "\"update /vol/storage_hfiomlc01/sys/update/pcfs\" -v -p " + Environment.GetEnvironmentVariable("SESSION_LAUNCH_CTRL_PORT"));
}
internal static int update_reflash()
{
#if DEBUG
Log.WriteLine("devkitmsg.update_reflash started.");
#endif
if (CAFEX_RECOVER_TEST == "1")
{
Console.WriteLine("devkitmsg_cmd: reflash /vol/storage_hfiomlc01/sys/update/nand -v -p ");
return 0;
}
return ProcessExecutor.DoProcess(exe_path, "\"reflash /vol/storage_hfiomlc01/sys/update/nand\" -v -p " + Environment.GetEnvironmentVariable("SESSION_LAUNCH_CTRL_PORT"));
}
internal static int update_nand()
{
#if DEBUG
Log.WriteLine("devkitmsg.update_nand started.");
#endif
if (CAFEX_RECOVER_TEST == "1")
{
Console.WriteLine("devkitmsg_cmd: update /vol/storage_hfiomlc01/sys/update/nand -v -p ");
return 0;
}
return ProcessExecutor.DoProcess(exe_path, "\"update /vol/storage_hfiomlc01/sys/update/nand\" -v -p " + Environment.GetEnvironmentVariable("SESSION_LAUNCH_CTRL_PORT"));
}
internal static int update(string update_path)
{
#if DEBUG
Log.WriteLine("devkitmsg.update started.");
#endif
if (string.IsNullOrEmpty(update_path))
{
return 1;
}
if (CAFEX_RECOVER_TEST == "1")
{
Console.WriteLine("devkitmsg_cmd: update " + update_path + " -v -p ");
return 0;
}
return ProcessExecutor.DoProcess(exe_path, "\"update " + update_path + "\" -v -p " + Environment.GetEnvironmentVariable("SESSION_LAUNCH_CTRL_PORT"));
}
internal static int reflash(string reflash_path)
{
#if DEBUG
Log.WriteLine("devkitmsg.reflash started.");
#endif
if (string.IsNullOrEmpty(reflash_path))
{
return 1;
}
if (CAFEX_RECOVER_TEST == "1")
{
Console.WriteLine("devkitmsg_cmd: reflash " + reflash_path + " -v -p ");
return 0;
}
return ProcessExecutor.DoProcess(exe_path, "\"reflash " + reflash_path + "\" -v -p " + Environment.GetEnvironmentVariable("SESSION_LAUNCH_CTRL_PORT"));
}
internal static int clr_usrdata()
{
#if DEBUG
Log.WriteLine("devkitmsg.clr_usrdata started.");
#endif
if (CAFEX_RECOVER_TEST == "1")
{
Console.WriteLine("devkitmsg_cmd: clr_usrdata -v -p ");
return 0;
}
return ProcessExecutor.DoProcess(exe_path, "clr_usrdata -v -p " + Environment.GetEnvironmentVariable("SESSION_LAUNCH_CTRL_PORT"));
}
internal static int sys_mode_dev()
{
#if DEBUG
Log.WriteLine("devkitmsg.sys_mode_dev started.");
#endif
return ProcessExecutor.DoProcess(exe_path, "\"sys_mode 1\" -v -p " + Environment.GetEnvironmentVariable("SESSION_LAUNCH_CTRL_PORT"));
}
internal static int sys_mode_prod()
{
#if DEBUG
Log.WriteLine("devkitmsg.sys_mode_prod started.");
#endif
return ProcessExecutor.DoProcess(exe_path, "\"sys_mode 0\" -v -p " + Environment.GetEnvironmentVariable("SESSION_LAUNCH_CTRL_PORT"));
}
internal static int sys_mode_test()
{
#if DEBUG
Log.WriteLine("devkitmsg.sys_mode_test started.");
#endif
return ProcessExecutor.DoProcess(exe_path, "\"sys_mode 2\" -v -p " + Environment.GetEnvironmentVariable("SESSION_LAUNCH_CTRL_PORT"));
}
internal static int standby_en(int value)
{
#if DEBUG
Log.WriteLine("devkitmsg.standby_en " + value);
#endif
return ProcessExecutor.DoProcess(exe_path, " 'standby_en 1' -v -p " + Environment.GetEnvironmentVariable("SESSION_LAUNCH_CTRL_PORT"));
}
}
static class cosdebug
{
#if TIMER
public static PerfTimer cosdebugSetupTime = new PerfTimer("cosdebugSetupTime");
#endif
#if false
private static Thread retry_thread = null;
private static ManualResetEvent stop_event = null;
private struct LaunchRetryParams
{
public int port;
public string TID_HI;
public string TID_LO;
public int retries;
public int timeout;
public int delay;
}
#endif
internal static void StreamDiscard(Stream stream)
{
try
{
int b;
stream.ReadTimeout = 500;
#if DEBUG
Console.Write("cafex: cosdebug.launch discarding:");
#endif
while ((b = stream.ReadByte()) != -1)
{
#if DEBUG
Console.Write(" {0:X2}", b);
#endif
}
}
catch (IOException)
{
// Eat this exception. The timeout just expired
}
finally
{
#if DEBUG
Console.WriteLine();
#endif
}
}
#if false
private static void launch_retry_thread(object data)
{
LaunchRetryParams lrparams = (LaunchRetryParams)data;
#if DEBUG
Log.WriteLine("cosdebug.launch_retry_thread started.");
Log.WriteLine("Params:");
Log.WriteLine(string.Format(" port: {0}.", lrparams.port));
Log.WriteLine(string.Format(" TID_HI: {0}.", lrparams.TID_HI));
Log.WriteLine(string.Format(" TID_LO: {0}.", lrparams.TID_LO));
Log.WriteLine(string.Format(" retries: {0}.", lrparams.retries));
Log.WriteLine(string.Format(" timeout: {0}.", lrparams.timeout));
#endif
cattoucan.ClearNotifications();
ManualResetEvent[] evtHandle = new ManualResetEvent[3];
evtHandle[0] = new ManualResetEvent(false);
cattoucan.AddNotification(Nintendo.SDSG.CatToucan.Terms.KI_PIREPARE_TITLE_ASYNC, evtHandle[0]);
evtHandle[1] = new ManualResetEvent(false);
cattoucan.AddNotification("System busy, try again", evtHandle[1]);
evtHandle[2] = stop_event;
for (int retry = 0; retry < lrparams.retries; retry++)
{
launch(lrparams.port, lrparams.TID_HI, lrparams.TID_LO);
long start = DateTime.Now.Ticks;
#if DEBUG
Log.WriteLine(string.Format("cosdebug.launch_retry_thread: waiting on handles {0} and {1}.", evtHandle[0].Handle, evtHandle[1].Handle));
#endif
switch (WaitHandle.WaitAny(evtHandle, lrparams.timeout))
{
case 0:
// Got the success message and we are done
Log.WriteLineRaw("cafex: Got successful title fast launch message.");
#if DEBUG
Log.WriteLine(string.Format("cosdebug.launch_retry_thread: waited {0}mS.", TimeSpan.FromTicks(DateTime.Now.Ticks - start).TotalMilliseconds));
#endif
evtHandle[0].Reset();
return;
case 1:
// Got the retry, so we continue
Log.WriteLineRaw(string.Format("cafex: Got system busy message after fast launch, waiting {0}mS and retrying...", lrparams.delay));
#if DEBUG
Log.WriteLine(string.Format("cosdebug.launch_retry_thread: waited {0}mS.", TimeSpan.FromTicks(DateTime.Now.Ticks - start).TotalMilliseconds));
Log.WriteLine(string.Format(" : delaying {0}mS.", lrparams.delay));
#endif
evtHandle[1].Reset();
Thread.Sleep(lrparams.delay);
break;
case 2:
#if DEBUG
Log.WriteLine("cosdebug.retry_launch_thread: Got stop event, returning.");
#endif
return;
case WaitHandle.WaitTimeout:
Log.WriteLineRaw(string.Format("cafex: Fastlaunch timeout of {0}mS exceeded waiting for title to start, exiting.", lrparams.timeout));
Environment.Exit((int)CAFEX_ERROR.RUN_FASTLAUNCH_FAILED);
return;
}
}
// Exhausted all of our tries
Log.WriteLineRaw(string .Format("cafex: Unable to fastlaunch title after {0} attempts, exiting.", lrparams.retries));
Environment.Exit((int)CAFEX_ERROR.RUN_FASTLAUNCH_FAILED);
}
internal static void launch_retry(int port, string TID_HI, string TID_LO, int retries, int timeout, int delay)
{
#if DEBUG
Log.WriteLine("cosdebug.launch_retry started.");
#endif
LaunchRetryParams lrparams = new LaunchRetryParams();
lrparams.port = port;
lrparams.TID_HI = TID_HI;
lrparams.TID_LO = TID_LO;
lrparams.retries = retries;
lrparams.timeout = timeout;
lrparams.delay = delay;
retry_thread = new Thread(cosdebug.launch_retry_thread);
stop_event = new ManualResetEvent(false);
retry_thread.Start(lrparams);
#if DEBUG
Log.WriteLine("cosdebug.launch_retry exited.");
#endif
}
public static void stop_retry(int timeout)
{
if (retry_thread != null)
{
stop_event.Set();
retry_thread.Join(timeout);
}
}
#endif
internal static int launch(int port, string TID_HI, string TID_LO)
{
#if DEBUG
Log.WriteLine("cosdebug.launch started.");
#endif
try
{
TcpClient client = new TcpClient("localhost", port);
NetworkStream stream = client.GetStream();
// Discard any input if anything is waiting after connect. COS sends some prompts after connect.
StreamDiscard(stream);
string message = string.Format("\rcos launch 0x{0} 0x{1}\r", TID_HI, TID_LO);
Log.WriteLineRaw(string.Format("cafex: sending '{0}' to the devkit using port {1}...", message.Trim(), port));
byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
#if DEBUG
Console.Write("cafex: cosdebug.launch write ({0} bytes):", data.Length);
for (int i = 0; i < data.Length; i++)
{
Console.Write(" 0x{0:X2}", data[i]);
}
Console.WriteLine();
#endif
stream.WriteTimeout = 5000;
stream.Write(data, 0, data.Length);
// Discard any input stream if anything is sent after the write.
StreamDiscard(stream);
client.Close();
}
catch (InvalidOperationException e)
{
Log.WriteLine(string.Format("cafex: cosdebug.launch: InvalidOperationException: {0}", e));
}
catch (ArgumentNullException e)
{
Log.WriteLine(string.Format("cafex: cosdebug.launch: ArgumentNullException: {0}", e));
}
catch (SocketException e)
{
Log.WriteLine(string.Format("cafex: cosdebug.launch: SocketException: {0}", e));
}
catch (IOException e)
{
Log.WriteLine(string.Format("cafex: cosdebug.launch: IOException: {0}", e));
}
return 0;
}
internal static bool try_launch(string TID, int index)
{
int retries = Convert.ToInt32(Program.CAFERUN_OPTION_FASTLAUNCH_RETRIES.value);
int timeout = Convert.ToInt32(Program.CAFERUN_OPTION_FASTLAUNCH_TIMEOUT.value);
int delay = Convert.ToInt32(Program.CAFERUN_OPTION_FASTLAUNCH_DELAY.value);
int port = Convert.ToInt32(Program.SESSION_NET_MANAGE_PORT.value);
string TID_HI = TID.Substring(index, 8);
string TID_LO = TID.Substring(index + 8, 8);
if (Program.CAFE_PROFILE.value != null && Program.CAFE_PROFILE.value == "1")
{
CafeXEventtLog.Instance.WriteToEventLog(562, string.Format("[{0}] [BEGIN] [CafeX] System is doing FAST RELAUNCH", DateTime.Now.ToString("HH:mm:ss:fff")));
}
cattoucan.BeginTitleFastSync();
for (index = 0; index < retries; ++index)
{
cattoucan.PrepareFastLaunch(delay);
launch(port, TID_HI, TID_LO);
if (cattoucan.SyncWithTitle(timeout, true))
{
return true;
}
#if DEBUG
Log.WriteLine("Retrying fast launch...");
#endif
}
cattoucan.FinishSync(true);
return false;
}
internal static bool ParseReceivedMessage(string sMessage, string sParseStr, string sArgs)
{
string[] msgArray = sMessage.Split(' ');
for (int i = 0; i < msgArray.Length; i++)
{
string str = msgArray[i];
if ((str.CompareTo(sParseStr) == 0) &&
((i + 1) < msgArray.Length) &&
(msgArray[i + 1].CompareTo(sArgs) == 0))
return true;
}
return false;
}
internal static string SendCOSCommandAndRecvReply(int iPort, string sCmd, string sArgs, int iRetries, int iTimeout, int iDelay)
{
StringBuilder completeMessage = new StringBuilder();
TcpClient client = null;
NetworkStream stream = null;
#if DEBUG
Log.WriteLine("cosdebug." + sCmd + " started.");
#endif
try
{
#if TIMER
Log.WriteLine("new TcpClient start");
cosdebugSetupTime.Report();
#endif
client = new TcpClient("localhost", iPort);
#if TIMER
Log.WriteLine("new TcpClient end");
cosdebugSetupTime.Report();
#endif
stream = client.GetStream();
// Discard any input if anything is waiting after connect. COS sends some prompts after connect.
//StreamDiscard(stream);
string message = string.Format("{0} {1}\r", sCmd, sArgs);
Log.WriteLineRaw(string.Format("cafex: sending '{0}' to the devkit using port {1}...", message.Trim(), iPort));
byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
#if DEBUG
Console.Write("cafex: cosdebug.{0} write ({1} bytes):", sCmd, data.Length);
for (int i = 0; i < data.Length; i++)
{
Console.Write(" 0x{0:X2}", data[i]);
}
Console.WriteLine();
#endif
stream.WriteTimeout = iTimeout;
stream.ReadTimeout = iTimeout;
stream.Write(data, 0, data.Length);
Thread.Sleep(iDelay);
if (stream.CanRead)
{
byte[] readBuffer = new byte[4096];
int readIndex = 0;
int numberOfBytesRead = 0;
int count = 0;
while (!stream.DataAvailable)
{
if (count == iRetries)
{
break;
}
Thread.Sleep(iDelay);
count++;
}
if (count <= iRetries || stream.DataAvailable)
{
// Incoming message may be larger than the buffer size.
while (stream.DataAvailable)
{
numberOfBytesRead = stream.Read(readBuffer, readIndex, readBuffer.Length);
completeMessage.AppendFormat("{0}", Encoding.ASCII.GetString(readBuffer, 0, numberOfBytesRead));
readIndex += numberOfBytesRead;
Thread.Sleep(200);
}
#if DEBUG
Console.Write("cafex: cosdebug.{0} read ({1}):", sCmd, completeMessage);
Console.WriteLine();
#endif
}
}
}
catch (InvalidOperationException e)
{
Log.WriteLine(string.Format("cafex: cosdebug.{0}: InvalidOperationException: {1}", sCmd, e));
}
catch (ArgumentNullException e)
{
Log.WriteLine(string.Format("cafex: cosdebug.{0}: ArgumentNullException: {1}", sCmd, e));
}
catch (SocketException e)
{
Log.WriteLine(string.Format("cafex: cosdebug.{0}: SocketException: {1}", sCmd, e));
}
catch (IOException e)
{
Log.WriteLine(string.Format("cafex: cosdebug.{0}: IOException: {1}", sCmd, e));
}
catch (Exception e)
{
Log.WriteLine(string.Format("cafex: cosdebug.{0}: Exception: {1}", sCmd, e));
}
finally
{
if (stream != null)
{
stream.Close();
}
if (client != null)
{
client.Close();
}
}
return completeMessage.ToString();
}
#if false
internal static string SendCOSCommandAndRecvReply(int iPort, string sCmd, string sArgs, int iRetries, int iTimeout, int iDelay)
{
#if DEBUG
Log.WriteLine("cosdebug." + sCmd + " started.");
#endif
Socket skt = null;
StringBuilder completeMessage = new StringBuilder();
try
{
IPAddress[] ipAddrList = Dns.GetHostEntry("localhost").AddressList;
skt = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
skt.Connect(ipAddrList, iPort);
if (skt.Connected)
{
string message = string.Format("{0} {1}\r", sCmd, sArgs);
Log.WriteLineRaw(string.Format("cafex: sending '{0}' to the devkit using port {1}...", message.Trim(), iPort));
byte[] bytesSent = System.Text.Encoding.ASCII.GetBytes(message);
#if DEBUG
Console.Write("cafex: cosdebug.{0} write ({1} bytes):", sCmd, bytesSent.Length);
for (int i = 0; i < bytesSent.Length; i++)
{
Console.Write(" 0x{0:X2}", bytesSent[i]);
}
Console.WriteLine();
#endif
skt.SendTimeout = iTimeout;
skt.ReceiveTimeout = iTimeout;
skt.Send(bytesSent, bytesSent.Length, 0);
Thread.Sleep(1000);
byte[] bytesRead = new byte[4096];
int numberOfBytesRead = 0;
numberOfBytesRead = skt.Receive(bytesRead, bytesRead.Length, 0);
completeMessage.AppendFormat("{0}", Encoding.ASCII.GetString(bytesRead, 0, numberOfBytesRead));
#if DEBUG
Console.Write("cafex: cosdebug.{0} read ({1}):", sCmd, completeMessage);
Console.WriteLine();
#endif
}
}
catch (InvalidOperationException e)
{
Log.WriteLine(string.Format("cafex: cosdebug.{0}: InvalidOperationException: {1}", sCmd, e));
}
catch (ArgumentNullException e)
{
Log.WriteLine(string.Format("cafex: cosdebug.{0}: ArgumentNullException: {1}", sCmd, e));
}
catch (SocketException e)
{
Log.WriteLine(string.Format("cafex: cosdebug.{0}: SocketException: {1}", sCmd, e));
}
catch (IOException e)
{
Log.WriteLine(string.Format("cafex: cosdebug.{0}: IOException: {1}", sCmd, e));
}
finally
{
if (skt != null)
{
skt.Close();
}
}
return completeMessage.ToString();
}
#endif
internal static string RecvReply(int iPort, int iTimeout)
{
try
{
TcpClient client = new TcpClient("localhost", iPort);
NetworkStream stream = client.GetStream();
if (stream.CanRead)
{
byte[] readBuffer = new byte[4096];
StringBuilder completeMessage = new StringBuilder();
int numberOfBytesRead = 0;
int count = 10;
while (!stream.DataAvailable)
{
if (count == 0)
{
break;
}
Thread.Sleep(10);
count--;
}
if (count > 0 || stream.DataAvailable)
{
// Incoming message may be larger than the buffer size.
do
{
numberOfBytesRead = stream.Read(readBuffer, 0, readBuffer.Length);
completeMessage.AppendFormat("{0}", Encoding.ASCII.GetString(readBuffer, 0, numberOfBytesRead));
} while (stream.DataAvailable);
#if DEBUG
Console.Write("cafex: cosdebug.RecvReply read ({0}):", completeMessage);
Console.WriteLine();
#endif
}
client.Close();
return completeMessage.ToString();
}
client.Close();
return string.Empty;
}
catch (InvalidOperationException e)
{
Log.WriteLine(string.Format("cafex: cosdebug.RecvReply: InvalidOperationException: {0}", e));
}
catch (ArgumentNullException e)
{
Log.WriteLine(string.Format("cafex: cosdebug.RecvReply: ArgumentNullException: {0}", e));
}
catch (SocketException e)
{
Log.WriteLine(string.Format("cafex: cosdebug.RecvReply: SocketException: {0}", e));
}
catch (IOException e)
{
Log.WriteLine(string.Format("cafex: cosdebug.RecvReply: IOException: {0}", e));
}
return string.Empty;
}
internal static bool SendKillRestartAndResponse(int iPort, int iRetries, int iTimeout, int iDelay)
{
string args = Convert.ToString((int)DateTime.Now.Ticks);
#if TIMER
Log.WriteLine("SendCOSCommandAndRecvReply start");
cosdebugSetupTime.Reset();
cosdebugSetupTime.Start();
#endif
string reply = SendCOSCommandAndRecvReply(iPort,
"cos killrestart",
args,
iRetries,
iTimeout,
iDelay);
#if TIMER
Log.WriteLine("SendCOSCommandAndRecvReply end");
cosdebugSetupTime.Stop();
#endif
if (string.IsNullOrEmpty(reply))
{
return false;
}
bool bFoundAck = ParseReceivedMessage(reply, "kill_ack", args);
bool bFoundDone = ParseReceivedMessage(reply, "kill_done", args);
if (bFoundAck && bFoundDone)
return true;
//This happens rarely
if (bFoundAck && !bFoundDone)
{
Thread.Sleep(iDelay);
reply = RecvReply(iPort, iDelay);
if (string.IsNullOrEmpty(reply))
return false;
bFoundDone = ParseReceivedMessage(reply, "kill_done", args);
if (bFoundDone)
return true;
}
return (bFoundAck && bFoundDone);
}
}
///
/// This class encapsulates the functionality of the synctool used by CafeX.
///
static class synctool
{
static string exe_path = "\"" + Environment.GetEnvironmentVariable("CAFE_ROOT") + "\\system\\bin\\tool\\synctool.exe" + "\"";
internal static int sync(string cfg, string src, string dst, string log)
{
#if DEBUG
Log.WriteLine("synctool.sync started.");
#endif
FileUtil.AddQuotes(ref src);
FileUtil.AddQuotes(ref dst);
string output = string.Empty;
int ret = ProcessExecutor.DoProcess_GetOutput(exe_path, "-cfgxml " + cfg + " " + src + " " + dst + " ", out output);
FileStream fs = new FileStream(log, FileMode.Create);
StreamWriter sw = new StreamWriter(fs);
sw.Write(output);
sw.Flush();
sw.Close();
return ret;
}
}
///
/// This class encapsulates the functionality of the pcfsserversync tool used by CafeX.
///
static class PCFSServerSync
{
static string exe_path = Environment.GetEnvironmentVariable("CAFE_ROOT") + "\\system\\bin\\tool\\PCFSServerSync.exe";
internal static int sync(string date, string elf, string mapping)
{
#if DEBUG
Log.WriteLine("PCFSServerSync.sync started.");
#endif
return ProcessExecutor.DoProcess(exe_path, "-comment \"cafex run: ----- [" + date + "] Launching " + Path.GetFileName(elf) + " -----\" " + Environment.GetEnvironmentVariable("PCFS_HEADLESS_EMUL") + " -softlaunch -message " + mapping);
}
internal static int wait_sync(string date)
{
#if DEBUG
Log.WriteLine("PCFSServerSync.wait_sync started.");
#endif
return ProcessExecutor.DoProcess(exe_path, "-wait 25000 " + Environment.GetEnvironmentVariable("PCFS_HEADLESS_EMUL") + " -comment cafex run: [" + date + "] ----- Hard-launch Startup Complete -----");
}
}
///
/// This class encapsulates the functionality of the ImageUploader tool used by CafeX.
///
static class ImageUploader
{
static string exe_path = Environment.GetEnvironmentVariable("MION_BRIDGE_TOOLS") + "\\ImageUploader.exe";
internal static int upload(string ip, int bankno, string wumad_file)
{
#if DEBUG
Log.WriteLine("ImageUploader.upload started.");
#endif
DataReceivedEventHandler callback = (sender, e) =>
{
if (e != null && e.Data != null)
{
Console.WriteLine(e.Data);
}
};
return ProcessExecutor.StartProcess_RedirectOutput(exe_path, string.Format("-ip {0} -upload {1} -w {2}", ip, bankno, wumad_file), callback, true);
}
}
///
/// This class encapsulates the functionality of the winmakebsf tool used by CafeX.
///
static class makebsf
{
static string exe_path = "\"" + Environment.GetEnvironmentVariable("CAFE_ROOT") + "\\system\\bin\\tool\\winmakebsf.exe" + "\"";
internal static int make(string quiet, string bsf_file, string flags)
{
#if DEBUG
Log.WriteLine("makebsf.make started.");
#endif
FileUtil.AddQuotes(ref bsf_file);
string output = string.Empty;
int ret = ProcessExecutor.DoProcess_GetOutput(exe_path, quiet + " -i 0x20008000 -j 0x20008800 -o " + bsf_file + " -f " + flags, out output);
if (!string.IsNullOrEmpty(output))
{
Console.Write(output);
}
return ret;
}
}
///
/// This class encapsulates the functionality of the winmakedlf tool used by CafeX.
///
static class makedlf
{
static string exe_path = "\"" + Environment.GetEnvironmentVariable("CAFE_ROOT") + "\\system\\bin\\tool\\winmakedlf.exe" + "\"";
internal static int make(string quiet, string file_list, string output_dlf_file, out string commandResult)
{
#if DEBUG
Log.WriteLine("makedlf.make started.");
#endif
commandResult = string.Empty;
FileUtil.AddQuotes(ref file_list);
FileUtil.AddQuotes(ref output_dlf_file);
return ProcessExecutor.DoProcess_GetOutput(exe_path, quiet + " -p " + file_list + " -l 0x0,0x80000,0x90000 -o " + output_dlf_file, out commandResult);
}
}
///
/// This class encapsulates the functionality of the makewumaddlf tool used by CafeX.
///
static class makewumaddlf
{
static string exe_path = "\"" + Environment.GetEnvironmentVariable("CAFE_ROOT") + "\\system\\bin\\tool\\mastering\\makewumaddlf.exe" + "\"";
internal static int make(string wumadFile, string extractionFolder, string output_dlf_file)
{
#if DEBUG
Log.WriteLine("make_wumad_dlf make started.");
#endif
FileUtil.AddQuotes(ref extractionFolder);
FileUtil.AddQuotes(ref output_dlf_file);
// In case the file is null, adjust the parameters accordingly
if (!string.IsNullOrEmpty(wumadFile))
{
FileUtil.AddQuotes(ref wumadFile);
wumadFile = wumadFile + " ";
}
DataReceivedEventHandler callback = (sender, e) =>
{
if (e != null && e.Data != null)
{
Console.WriteLine(e.Data);
}
};
return ProcessExecutor.StartProcess_RedirectOutput(exe_path, wumadFile + extractionFolder + " " + output_dlf_file, callback, true);
}
}
///
/// This class encapsulates the functionality of the cafemakedlf tool used by CafeX.
///
static class cafemakedlf
{
static string exe_path = "\"" + Environment.GetEnvironmentVariable("CAFE_ROOT") + "\\system\\bin\\tool\\mastering\\cafemakedlf.exe" + "\"";
internal static int make(string default_ddf, string common_ddf_file, string individual_ddf_file, string options)
{
#if DEBUG
Log.WriteLine("cafemakedlf.make started.");
#endif
FileUtil.AddQuotes(ref default_ddf);
FileUtil.AddQuotes(ref common_ddf_file);
string arguments = "-uf " + default_ddf + " " + common_ddf_file + " ";
if (individual_ddf_file != null)
{
FileUtil.AddQuotes(ref individual_ddf_file);
arguments += " " + individual_ddf_file;
}
if (options != null)
{
arguments += " " + options;
}
return ProcessExecutor.DoProcess(exe_path, arguments);
}
}
///
/// This class encapsulates the functionality of the multi compiler used by CafeX.
///
static class multi
{
static string exe_path = "\"" + Environment.GetEnvironmentVariable("GHS_ROOT") + "\\multi.exe" + "\"";
internal static int start(string multi_connect, string cafe_multi_init, string multielf)
{
#if DEBUG
Log.WriteLine("multi.start started.");
#endif
String ProfCmd = "";
String ProfComment = Path.GetFileName(multielf); // change wacks so it prints out as a string nicely...
FileUtil.AddQuotes(ref multielf);
if (Program.CAFE_PROFILE.value != null && Program.CAFE_PROFILE.value == "1")
{
CafeXEventtLog.Instance.WriteToEventLog(900, DateTime.Now, ProfComment);
String ProfFunc = "eventCreate2 /id 901 /d " + ProfComment;
// the ugliest line of code in the entire world
ProfCmd = "-cmd \"python -b -s \\\"os.system('" + ProfFunc + "')\\\"\" ";
}
String ExeCmd = "-cmd prepare_target " + multi_connect + " " + cafe_multi_init + " " + ProfCmd + multielf;
// Console.WriteLine("\n---------------\n" + exe_path + " " + ExeCmd + "\n-------------\n");
return ProcessExecutor.DoProcess_DontWait(exe_path, ExeCmd);
}
}
internal enum SyncStage
{
None,
Busy,
Boot,
TitleSoft,
TitleFast,
Menu,
}
///
/// This class emulates the functionality of the cattoucan tool.
///
static class cattoucan
{
private static List> notifyList = new List>();
private static List>> callbackList = new List>>();
internal static SyncStage sync = SyncStage.None;
private static Nintendo.SDSG.CatToucan toucan;
private static EventWaitHandle evtHandle = null;
private static Thread cattoucanThread = null;
private static Nintendo.SDSG.CatToucan.ExitReason exit_reason = null;
static void outputThread(object arg)
{
// Start relaying data
exit_reason = toucan.RelaySerialCharacters(Program.quiet_mode ?
TextWriter.Null : Console.Out,
true);
lock (cattoucanThread)
{
toucan = null;
}
// We don't expect for toucan to exit if we're trying to sync something
if (sync != SyncStage.None)
{
Console.WriteLine("cafex {0}: WARNING: CatToucan exited during sync! Cafex Code={1}, App Code={2}, Sync={3}",
Program.command,
exit_reason.reason,
exit_reason.AppExitCode,
sync);
}
#if DEBUG
Log.WriteLine("cattoucan exiting with reason = " + exit_reason.reason);
Log.WriteLine("Application exited with code " + exit_reason.AppExitCode);
#endif
if (exit_reason.reason == Nintendo.SDSG.CatToucan.ExitReason.Reason.StoppedDueToDroppedConnection)
{
Console.WriteLine("cafex : connection to CAT-DEV on port {0} failed or was dropped.", arg);
}
}
static void createCattoucan(bool exclusive_match)
{
string port = Program.SESSION_DEBUG_OUT_PORT.value;
#if DEBUG
Log.WriteLine("cattoucan.start started. port=" + port);
#endif
if (port.StartsWith("localhost:"))
{
port = port.Remove(0, 10);
}
#if DEBUG
Log.WriteLine("cattoucan starting to read from port " + port);
#endif
toucan = new Nintendo.SDSG.CatToucan(Convert.ToInt32(port));
foreach (KeyValuePair kvp in notifyList)
{
toucan.AddNotification(kvp);
}
foreach (KeyValuePair> kvp in callbackList)
{
toucan.AddNotification(kvp);
}
toucan.ExclusiveMatch = exclusive_match;
cattoucanThread = new Thread(new ParameterizedThreadStart(outputThread));
cattoucanThread.Start(port);
}
internal static int start(bool no_console, bool exclusive_match)
{
if (Program.quiet_mode)
{
stopCattoucan();
#if DEBUG
Log.WriteLine("cattoucan DID NOT START because quiet is true");
#endif
return 0;
}
if (cattoucanThread != null)
{
lock (cattoucanThread)
{
if (toucan != null)
{
toucan.ExclusiveMatch = exclusive_match;
}
}
}
else
{
createCattoucan(exclusive_match);
}
#if DEBUG
Log.WriteLine("Joining cattoucan thread from start");
#endif
cattoucanThread.Join();
cattoucanThread = null;
#if DEBUG
Log.WriteLine("Async cattoucan exited from start");
#endif
return exit_reason.AppExitCode;
}
internal static int stopCattoucan()
{
if (cattoucanThread == null)
{
return -1;
}
#if DEBUG
Log.WriteLine("Stopping relay");
#endif
lock (cattoucanThread)
{
if (toucan != null)
{
toucan.StopRelay();
}
}
#if DEBUG
Log.WriteLine("Joining cattoucan thread from stop");
#endif
cattoucanThread.Join();
cattoucanThread = null;
#if DEBUG
Log.WriteLine("Async cattoucan stopped");
#endif
if (exit_reason.reason != Nintendo.SDSG.CatToucan.ExitReason.Reason.StoppedDueToDroppedConnection)
{
return exit_reason.AppExitCode;
}
return 0;
}
internal static void AddNotification(string match, ManualResetEvent evt)
{
KeyValuePair kvp = new KeyValuePair(match, evt);
if (toucan != null)
{
toucan.AddNotification(kvp);
}
notifyList.Add(kvp);
}
private static void closeEvent(ref EventWaitHandle evtHandle)
{
if (evtHandle != null)
{
evtHandle.Close();
evtHandle = null;
}
}
internal static void AddNotification(string match, Func callback)
{
KeyValuePair> kvp = new KeyValuePair>(match, callback);
if (toucan != null)
{
toucan.AddNotification(kvp);
}
int already = callbackList.IndexOf(kvp);
if (already >= 0)
{
#if DEBUG
Log.WriteLine("Overriding existing handler for '{0}'!", match);
#endif
callbackList.RemoveAt(already);
}
callbackList.Add(kvp);
}
internal static void ClearNotifications()
{
if (toucan != null)
{
toucan.ClearNotifications();
}
notifyList.Clear();
callbackList.Clear();
}
internal static void BeginBootSync()
{
sync = SyncStage.None;
// Prepare cattoucan to wait for boot syncronization
closeEvent(ref evtHandle);
evtHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
if (cattoucanThread == null)
{
createCattoucan(false);
}
}
internal static bool SendSyncRequest(int timeout)
{
if (evtHandle == null)
{
return false;
}
lock (cattoucanThread)
{
sync = SyncStage.None;
}
// Reset the handle and try sending the request
evtHandle.Reset();
sync = SyncStage.Boot;
return devkitmsg.help(timeout) == 0;
}
internal static void SignalBootEvent()
{
lock (cattoucanThread)
{
if (sync == SyncStage.Boot)
{
sync = SyncStage.None;
evtHandle.Set();
}
}
}
internal static bool SyncWithBoot(int timeout)
{
if (evtHandle == null)
{
return false;
}
// Wait for this message to be triggered
Console.WriteLine("\nCafex {0}: Waiting for response...", Program.command);
return evtHandle.WaitOne(timeout);
}
internal static void BeginTitleSoftSync()
{
sync = SyncStage.None;
closeEvent(ref evtHandle);
evtHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
sync = SyncStage.TitleSoft;
if (cattoucanThread == null)
{
createCattoucan(false);
}
}
internal static void BeginTitleFastSync()
{
stopCattoucan();
closeEvent(ref evtHandle);
evtHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
sync = SyncStage.None;
createCattoucan(true);
}
internal static void PrepareFastLaunch(int delay)
{
if (sync != SyncStage.Busy)
{
delay = 0;
}
lock (cattoucanThread)
{
sync = SyncStage.None;
}
if (delay != 0)
{
Thread.Sleep(delay);
}
sync = SyncStage.TitleFast;
}
internal static void SignalTitleEvent(SyncStage next)
{
lock (cattoucanThread)
{
if (sync != SyncStage.None)
{
#if DEBUG
Log.WriteLine("Signaling title event, next=" + next.ToString());
#endif
sync = next;
evtHandle.Set();
}
}
}
internal static bool SyncWithTitle(int timeout, bool stop)
{
if (evtHandle != null)
{
// Wait for the event to be called
while (evtHandle.WaitOne(timeout))
{
if (sync == SyncStage.None)
{
// Successfully called
FinishSync(stop);
return true;
}
if (sync == SyncStage.Busy)
{
return false;
}
}
}
return false;
}
internal static void BeginMenuSync()
{
if (cattoucanThread != null)
{
Monitor.Enter(cattoucanThread);
}
sync = SyncStage.None;
if (cattoucanThread != null)
{
Monitor.Exit(cattoucanThread);
}
// Prepare cattoucan to wait for menu syncronization
closeEvent(ref evtHandle);
evtHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
sync = SyncStage.Menu;
if (cattoucanThread == null)
{
createCattoucan(false);
}
}
internal static void SignalMenuEvent()
{
lock (cattoucanThread)
{
sync = SyncStage.None;
evtHandle.Set();
}
}
internal static void SyncWithMenu()
{
if (evtHandle != null)
{
// Wait for the event to be called
evtHandle.WaitOne();
FinishSync(true);
}
}
internal static void FinishSync(bool stop)
{
// Clear the sync flags
if (cattoucanThread != null)
{
Monitor.Enter(cattoucanThread);
sync = SyncStage.None;
if (stop)
{
Monitor.Exit(cattoucanThread);
// Stop async cattoucan
stopCattoucan();
}
}
else
{
// Do not try and exit lock
stop = true;
}
// Delete the event handles
closeEvent(ref evtHandle);
if (!stop)
{
Monitor.Exit(cattoucanThread);
}
}
}
///
/// This class encapsulates the functionality required to set the COM ports.
///
static class cmd
{
static string exe_path = "cmd.exe";
internal static int mode(int comport)
{
#if DEBUG
Log.WriteLine("cmd.mode started.");
#endif
return ProcessExecutor.DoProcess(exe_path, "mode.com com" + comport + ": BAUD=115200 PARITY=N DATA=8 STOP=1 to=off xon=off odsr=off octs=on dtr=off rts=on idsr=off");
}
}
///
/// This class encapsulates the functionality of the monitor tool used by CafeX.
///
static class monitor
{
static string cygwin_monitor_exe_path = "\"" + Environment.GetEnvironmentVariable("CAFE_ROOT") + "\\system\\bin\\tool\\monitor.exe" + "\"";
static readonly int MONITOR_FLUSH_TIME = 1;
private static string GetExeName()
{
string monitor_or_temp_exe_path;
if (FileUtil.RunningFromCygwin)
{
// take the monitor from cygwin
monitor_or_temp_exe_path = cygwin_monitor_exe_path;
}
else
{
monitor_or_temp_exe_path = FileUtil.ExpandExecutable(CafeX.Properties.Resources.win_monitor, "win_monitor.exe");
}
return monitor_or_temp_exe_path;
}
internal static int start(string str_opt, string test_str, string timeout, string rc_file, string log)
{
#if DEBUG
Log.WriteLine("monitor.start started.");
#endif
string output = string.Empty;
int ret = ProcessExecutor.DoProcess_GetOutput(GetExeName(), str_opt + " \"" + test_str + "\" -t " + timeout + " -p " + Environment.GetEnvironmentVariable("SESSION_DEBUG_OUT_PORT"), out output);
FileStream fs = new FileStream(rc_file, FileMode.Create);
StreamWriter sw = new StreamWriter(fs);
sw.WriteLine(ret);
sw.Flush();
sw.Close();
fs = new FileStream(log, FileMode.Create);
sw = new StreamWriter(fs);
sw.WriteLine(output);
sw.Flush();
sw.Close();
return ret;
}
internal static int start_redirectionCallback(string str_opt, string test_str, string timeout, DataReceivedEventHandler callback)
{
#if DEBUG
Log.WriteLine("start_redirectionCallback started.");
#endif
string output = string.Empty;
int ret = ProcessExecutor.StartProcess_RedirectOutput(GetExeName(), str_opt + " \"" + test_str + "\" -t " + timeout + " -p " + Environment.GetEnvironmentVariable("SESSION_DEBUG_OUT_PORT"), callback, true);
return ret;
}
internal static void flush()
{
#if DEBUG
Log.WriteLine("monitor.flush started.");
#endif
int ret = ProcessExecutor.DoProcess(GetExeName(), "-t " + MONITOR_FLUSH_TIME + " -s " + MONITOR_FLUSH_TIME);
#if DEBUG
Log.WriteLine("monitor.flush returned = " + ret);
#endif
}
}
static class mionsig
{
static class HTML_CONTENT_TYPE
{
public const string APP_FORM_URL_ENCODED = "application/x-www-form-urlencoded";
}
static class HTML_METHOD
{
public const string POST = "POST";
}
internal const string MION_SIG_FMT_ADDR = "http://{0}/signal_get.cgi";
internal const string MION_ADMIN_USER = "mion";
internal const string MION_ADMIN_PASS = "/Multi_I/O_Network/";
internal const string HTML_TAG_END = "