#pragma warning restore CS0162 /* * /// <summary> * /// Leaves the serial connection open * /// Will attempt to detect /HALT notifications from Unirom * /// and catch crash/exception events * /// </summary> * public static void DoMonitor(){ * * // Note: * // Mono hasn't implemented the activeSerial.ReceivedBytesThreshold methods yet * // so we can't really use events. Instead the max we'll wait is 1ms to enter the * // tight inner loop. Should be fine if you're not filling a ~2kb buffer in 1ms * * // a rolling buffer of the last 4 things recieved * string lastMonitorBytes = ""; * * while (true) * { * * while (activeSerial.BytesToRead > 0) * { * * // echo things to the screen * * int responseByte = activeSerial.ReadByte(); * Console.Write((char)(responseByte)); * * // check if the PSX has crashed * * // TODO: use a more appropriate data collection, lol * lastMonitorBytes += (char)responseByte; * while ( lastMonitorBytes.Length > 4 ) * lastMonitorBytes = lastMonitorBytes.Remove( 0, 1 ); * * if ( lastMonitorBytes == "HLTD" ){ * * while ( true ){ * * Console.WriteLine( "\nThe PSX may have crashed, enter debug mode? (y/n)" ); * Console.WriteLine( "(Also starts a TCP/SIO bridge on port 3333." ); * ConsoleKeyInfo c = Console.ReadKey(); * if ( c.KeyChar.ToString().ToLowerInvariant() == "y" ){ * GDB.GetRegs(); * GDB.DumpRegs(); * GDB.Init( 3333 ); * return; * } else { * Console.WriteLine( "\nReturned to monitor mode." ); * break; * } * * } * * * } * * } * * Thread.Sleep( 1 ); * * } * * * } */ /// <summary> /// Puts Unirom into /debug mode and wipes everything from the end of the kernel /// to the stack, where it will crash. // 0x80010000 -> 0x801FFF?? /// </summary> /// <param name="wipeValue">32 bit value to fill ram with</param> /// <returns></returns> public static bool Command_WipeMem(UInt32 wipeAddress, UInt32 wipeValue) { // if it returns true, we might enter /m (monitor) mode, etc if ( !TransferLogic.ChallengeResponse(CommandMode.DEBUG) ) { Console.WriteLine("Couldn't determine if Unirom is in debug mode."); return(false); } Thread.Sleep(200); byte[] buffer = new byte[0x80200000 - wipeAddress]; // just shy of 2MB for (int i = 0; i < buffer.Length / 4; i++) { BitConverter.GetBytes(wipeValue).CopyTo(buffer, i * 4); } Command_SendBin(wipeAddress, buffer); // It won't return. return(true); }
public static void DebugInit(UInt32 localPort) { Console.WriteLine("Checking if Unirom is in debug mode..."); // if it returns true, we might enter /m (monitor) mode, etc if ( !TransferLogic.ChallengeResponse(CommandMode.DEBUG) ) { Console.WriteLine("Couldn't determine if Unirom is in debug mode."); return; } Console.WriteLine("Grabbing initial state..."); TransferLogic.Command_DumpRegs(); Console.WriteLine("Monitoring sio..."); Console.WriteLine("********************** WARNING ***************************"); Console.WriteLine("THE TCP BRIDGE DOES NOT CURRENTLY ACCEPT COMMANDS FROM GDB"); Console.WriteLine("********************** ******* ***************************"); Init(localPort); }
// // Memcard Functions // /// <summary> /// Writes an entire memcard's contents /// </summary> /// <param name="inCard">0/1</param> public static bool Command_MemcardUpload(UInt32 inCard, byte[] inFile) { if (!TransferLogic.ChallengeResponse(CommandMode.MCUP)) { return(Error("No response from Unirom. Are you using 8.0.E or higher?")); } Console.WriteLine("Uploading card data..."); // send the card number activeSerial.Write(BitConverter.GetBytes(inCard), 0, 4); // file size in bytes, let unirom handle it activeSerial.Write(BitConverter.GetBytes(inFile.Length), 0, 4); activeSerial.Write(BitConverter.GetBytes(CalculateChecksum(inFile)), 0, 4); if (TransferLogic.WriteBytes(inFile, false)) { Console.WriteLine("File uploaded, check your screen..."); } else { return(Error("Couldn't upload to unirom - no write attempt will be made", false)); } return(true); }
/// <summary> /// Reads and dumps a memcard to disc /// </summary> /// <param name="inCard">0/1</param> public static bool Command_MemcardDownload(UInt32 inCard, string fileName) { if (!TransferLogic.ChallengeResponse(CommandMode.MCDOWN)) { return(Error("No response from Unirom. Are you using 8.0.E or higher?")); } // send the card number activeSerial.Write(BitConverter.GetBytes(inCard), 0, 4); Console.WriteLine("Reading card to ram..."); // it'll send this when it's done dumping to ram if (!TransferLogic.WaitResponse("MCRD", false)) { return(Error("Please see screen or SIO for error!")); } Console.WriteLine("Ready, reading...."); UInt32 addr = TransferLogic.read32(); Console.WriteLine("Data is 0x" + addr.ToString("x")); UInt32 size = TransferLogic.read32(); Console.WriteLine("Size is 0x" + size.ToString("x")); Console.WriteLine("Dumping..."); byte[] lastReadBytes = new byte[size]; TransferLogic.ReadBytes(addr, size, lastReadBytes); if (System.IO.File.Exists(fileName)) { string newFilename = fileName + GetSpan().TotalSeconds.ToString(); Console.Write("\n\nWARNING: Filename " + fileName + " already exists! - Dumping to " + newFilename + " instead!\n\n"); fileName = newFilename; } try { File.WriteAllBytes(fileName, lastReadBytes); } catch (Exception e) { return(Error("Couldn't write to the output file + " + fileName + " !\nThe error returned was: " + e, false)); } Console.WriteLine("File written to: " + fileName); Console.WriteLine("It is raw .mcd format used by PCSX-redux, no$psx, etc"); return(true); }
// final method that handles msg from the source, whether it's from wndproc or callbacks void HandleSourceMsg(Message msg) { PlatformInfo.Current.Log.Debug("Got TWAIN msg " + msg); switch (msg) { case Message.XferReady: if (State < 6) { State = 6; } TransferLogic.DoTransferRoutine(this); break; case Message.DeviceEvent: TWDeviceEvent de; var rc = ((ITwainSessionInternal)this).DGControl.DeviceEvent.Get(out de); if (rc == ReturnCode.Success) { SafeSyncableRaiseOnEvent(OnDeviceEvent, DeviceEvent, new DeviceEventArgs(de)); } break; case Message.CloseDSReq: case Message.CloseDSOK: DisableReason = msg; // even though it says closeDS it's really disable. // dsok is sent if source is enabled with uionly // some sources send this at other states so do a step down if (State > 5) { // rather than do a close here let the transfer logic handle the close down now //ForceStepDown(4); _closeRequested = true; } else if (State == 5) { // needs this state check since some source sends this more than once ((ITwainSessionInternal)this).DisableSource(); } break; } }
public static bool SetRegs() { // read the pointer to TCB[0] byte[] ptrBuffer = new byte[4]; if (!TransferLogic.ReadBytes(0x80000110, 4, ptrBuffer)) { return(false); } UInt32 tcbPtr = BitConverter.ToUInt32(ptrBuffer, 0); Console.WriteLine("TCB PTR " + tcbPtr.ToString("X")); // Convert regs back to a byte array and bang them back out byte[] tcbBytes = new byte[TCB_LENGTH_BYTES]; Buffer.BlockCopy(tcb.regs, 0, tcbBytes, 0, TCB_LENGTH_BYTES); TransferLogic.Command_SendBin(tcbPtr, tcbBytes); return(true); }
public static bool GetRegs() { // read the pointer to TCB[0] byte[] ptrBuffer = new byte[4]; if (!TransferLogic.ReadBytes(0x80000110, 4, ptrBuffer)) { return(false); } UInt32 tcbPtr = BitConverter.ToUInt32(ptrBuffer, 0); Console.WriteLine("TCB PTR " + tcbPtr.ToString("X")); byte[] tcbBytes = new byte[TCB_LENGTH_BYTES]; if (!TransferLogic.ReadBytes(tcbPtr, (int)GPR.COUNT * 4, tcbBytes)) { return(false); } Buffer.BlockCopy(tcbBytes, 0, tcb.regs, 0, tcbBytes.Length); return(true); }
public static void Init() { Console.WriteLine("Checking if Unirom is in debug mode..."); // if it returns true, we might enter /m (monitor) mode, etc if ( !TransferLogic.ChallengeResponse(CommandMode.DEBUG) ) { Console.WriteLine("Couldn't determine if Unirom is in debug mode."); return; } Console.WriteLine("Grabbing initial state..."); TransferLogic.Command_DumpRegs(); Console.WriteLine("Monitoring sio..."); InitSocket(); MonitorSIOAndSocket(); }
/// <summary> /// We've parsed everything from stdin, let's start processing it. /// </summary> /// <returns>success?</returns> private static bool DoStuff() { PrintUsage(true); // // Process the secondary/standalone commands // /fast, /slow, /m, etc // if (enterFastMode) { // start in normal mode and send a "FAST" command // unirom doesn't have to respond, so if it's already in fast mode, no biggie if (!NewSIO(SIOSPEED.SLOW)) { return(false); } // The bytes "FAST" with no null terminator activeSerial.Write(BitConverter.GetBytes(0x54534146), 0, 4); Thread.Sleep(100); // Switch to fast if (!NewSIO(SIOSPEED.FAST)) { return(false); } } else if (enterSlowMode) { // As above, but returns to 115200 so you don't have to 'nops /fast' all the time // going to assume Unirom's in fast mode.. so start there and negotiate back down if (!NewSIO(SIOSPEED.FAST)) { return(false); } // The bytes "FAST" with no null terminator activeSerial.Write(BitConverter.GetBytes(0x574F4C53), 0, 4); Thread.Sleep(100); // Now switch back if (!NewSIO(SIOSPEED.SLOW)) { return(false); } } else { if (!NewSIO(SIOSPEED.SLOW)) { return(false); } } // If we got /m on its own if (monitorComms && argCommand == CommandMode.NOT_SET) { GDB.MonitorSerialToSocket(); return(true); } // we just did 'nops /fast /m' or something if (allArgsAreSecondary) { return(true); } // // A little cleanup... // if (usingCachedComPort) { Console.WriteLine("Using port " + argComPort + " from comport.txt\n"); } // Clear the SIO buffer incase the last program has been spamming Console.WriteLine("Emptying buffer... "); while (activeSerial.BytesToRead != 0) { Console.Write("" + (char)activeSerial.ReadByte()); } Console.WriteLine("...done!\n\n"); // // Process the primary commands // // Upload if (argCommand == CommandMode.SEND_EXE) { TransferLogic.Command_SendEXE(inFile); } if (argCommand == CommandMode.SEND_BIN) { TransferLogic.Command_SendBin(argAddr, inFile); } if (argCommand == CommandMode.SEND_ROM) { TransferLogic.Command_SendROM(argAddr, inFile); } // Program flow if (argCommand == CommandMode.JUMP_JMP) { TransferLogic.Command_JumpAddr(argAddr); } if (argCommand == CommandMode.JUMP_CALL) { TransferLogic.Command_CallAddr(argAddr); } // Pokeypoke if (argCommand == CommandMode.POKE8) { byte[] bytes = { (byte)(argValue & 0xFF) }; TransferLogic.Command_SendBin(argAddr, bytes); } if (argCommand == CommandMode.POKE16) { short shorty = (byte)(argValue & 0xFFFF); byte[] bytes = BitConverter.GetBytes(shorty); TransferLogic.Command_SendBin(argAddr, bytes); } if (argCommand == CommandMode.POKE32) { byte[] bytes = BitConverter.GetBytes(argValue); TransferLogic.Command_SendBin(argAddr, bytes); } // Utility if (argCommand == CommandMode.PING) { TransferLogic.ChallengeResponse(argCommand); } if (argCommand == CommandMode.RESET) { TransferLogic.WriteChallenge(argCommand.challenge()); } // Debug functions if (argCommand == CommandMode.DEBUG) { // if it returns true, we might enter /m (monitor) mode, etc if ( !TransferLogic.ChallengeResponse(argCommand) ) { return(false); } } if (argCommand == CommandMode.HALT) { TransferLogic.ChallengeResponse(argCommand); } if (argCommand == CommandMode.CONT) { TransferLogic.ChallengeResponse(argCommand); } if (argCommand == CommandMode.REGS) { TransferLogic.Command_DumpRegs(); } if (argCommand == CommandMode.SETREG) { if (!TransferLogic.Command_SetReg(argRegister, argValue)) { return(Error("Couldn't set reg " + argRegister + " to " + argValue, false)); } } // Hook functions if ( argCommand == CommandMode.HOOKREAD || argCommand == CommandMode.HOOKWRITE || argCommand == CommandMode.HOOKEXEC ) { if (TransferLogic.ChallengeResponse(argCommand)) { activeSerial.Write(BitConverter.GetBytes(argAddr), 0, 4); } } // Memory card if (argCommand == CommandMode.MCUP) { if (!TransferLogic.Command_MemcardUpload(argCard, inFile)) { return(false); } } if (argCommand == CommandMode.WIPEMEM) { if (!TransferLogic.Command_WipeMem(argAddr, argValue)) { return(false); } } if (argCommand == CommandMode.MCDOWN) { if (!TransferLogic.Command_MemcardDownload(argCard, argFileName)) { return(false); } } // Peek / Read functions if (argCommand == CommandMode.DUMP) { if (!TransferLogic.Command_Dump(argAddr, argSize)) { return(false); } } if (argCommand == CommandMode.WATCH) { TransferLogic.Watch(argAddr, argSize); return(true); } // // Major work in progress // if (argCommand == CommandMode.GDB) { GDB.DebugInit(argPort); } if (argCommand == CommandMode.BRIDGE) { GDB.Init(argPort); } // // All done, are we leaving the comms monitor open? // if (monitorComms) { GDB.MonitorSerialToSocket(); } else { Console.WriteLine("\n Bye!"); activeSerial.Close(); } return(true); } // void Transfer
private static bool Transfer() { PrintUsage(true); activeSerial = new SerialPort(argComPort, 115200, Parity.None, 8, StopBits.Two); // Required for e.g. SharkLink & Yaroze cable compat. Doesn't interfere with the 3-wire setups activeSerial.Handshake = Handshake.None; activeSerial.DtrEnable = true; activeSerial.RtsEnable = true; if (fastMode) { activeSerial.ReadTimeout = TIMEOUT; activeSerial.WriteTimeout = TIMEOUT; try { activeSerial.Open(); } catch (Exception exception) { Console.WriteLine("Error opening temporary serial port on " + argComPort + "!"); Console.WriteLine(exception.Message); return(false); } // The bytes "FAST" with no null terminator activeSerial.Write(BitConverter.GetBytes(0x54534146), 0, 4); Thread.Sleep(100); activeSerial.Close(); // We need to find a suitable overlap in frequencies which can be divided // from the Playstation's clock and from the FTDI/clone, with some wiggle room. // // PSX/libs uses whole integer divisions of 2073600 for anything over 115200 // giving us 518400 close to the half-megabyte mark. // // Most FTDI/clones seem to operate in 2xinteger divisions of 48mhz // giving us 510000 or 521000 close to the half-megabyte mark. e.g. (48m/47/2) or (48m/46/2) // // 5210000 (and even 518400) is a little fast, but the lower end // of things (510000 to 518300) seems pretty stable. // // note: psx @ 518400, pc @ 510000 // activeSerial = new SerialPort(argComPort, 510000, Parity.None, 8, StopBits.Two); // Required for e.g. SharkLink & Yaroze cable compat. Doesn't interfere with the 3-wire setups activeSerial.Handshake = Handshake.None; activeSerial.DtrEnable = true; activeSerial.RtsEnable = true; } // Now the main serial port activeSerial.ReadTimeout = TIMEOUT; activeSerial.WriteTimeout = TIMEOUT; try { activeSerial.Open(); } catch (Exception exception) { Console.WriteLine("Error opening the serial port on " + argComPort + "!"); Console.WriteLine(exception.Message); return(false); } // just lets us skip a ton of ifs if (monitorComms && argCommand == CommandMode.NOT_SET) { TransferLogic.DoMonitor(); return(true); } if (usingCachedComPort) { Console.WriteLine("Using port " + argComPort + " from comport.txt\n"); } // Clear the SIO buffer incase the last program has been spamming Console.WriteLine("Emptying buffer... "); while (activeSerial.BytesToRead != 0) { Console.Write("" + (char)activeSerial.ReadByte()); } Console.WriteLine("...done!\n\n"); if (argCommand == CommandMode.SEND_EXE) { TransferLogic.Command_SendEXE(argAddr, inFile, CalculateChecksum(inFile, true)); } if (argCommand == CommandMode.SEND_BIN) { TransferLogic.Command_SendBin(argAddr, inFile, CalculateChecksum(inFile)); } // Unirom 8 mode - requires a response after checking that // things will fit on the cart. if (argCommand == CommandMode.SEND_ROM) { TransferLogic.Command_SendROM(argAddr, inFile, CalculateChecksum(inFile)); } if (argCommand == CommandMode.RESET) { TransferLogic.WriteChallenge(argCommand.challenge()); } // Unirom 8.0.4 and up, enables kernel-resident SIO if (argCommand == CommandMode.DEBUG) { // if it returns true, we might enter /m (monitor) mode, etc if ( !TransferLogic.ChallengeResponse(argCommand) ) { return(false); } } if (argCommand == CommandMode.DUMP) { lastReadBytes = new byte[argSize]; TransferLogic.Command_DumpBytes(argAddr, argSize, lastReadBytes); string fileName = "DUMP_" + argAddr.ToString("X8") + "_to_" + argSize.ToString("X8") + ".bin"; if (System.IO.File.Exists(fileName)) { string newFilename = fileName + GetSpan().TotalSeconds.ToString(); Console.Write("\n\nWARNING: Filename " + fileName + " already exists! - Dumping to " + newFilename + " instead!\n\n"); fileName = newFilename; } try{ File.WriteAllBytes(fileName, lastReadBytes); } catch (Exception e) { Error("Couldn't write to the output file + " + fileName + " !\nThe error returned was: " + e, false); return(false); } } // DUMP if (argCommand == CommandMode.PING) { TransferLogic.ChallengeResponse(argCommand); } if (argCommand == CommandMode.GDB) { GDB.Init(); } if (argCommand == CommandMode.JUMP_JMP) { TransferLogic.Command_JumpAddr(argAddr); } if (argCommand == CommandMode.JUMP_CALL) { TransferLogic.Command_CallAddr(argAddr); } if (argCommand == CommandMode.HALT) { TransferLogic.ChallengeResponse(argCommand); } if (argCommand == CommandMode.CONT) { TransferLogic.ChallengeResponse(argCommand); } if (argCommand == CommandMode.REGS) { TransferLogic.Command_DumpRegs(); } if (argCommand == CommandMode.SETREG) { TransferLogic.Command_SetReg(argRegister, argAddr); } if (argCommand == CommandMode.WATCH) { TransferLogic.Watch(argAddr, argSize); return(true); } // WATCH if (monitorComms) { TransferLogic.DoMonitor(); } else { Console.WriteLine("\n This is where we part ways!"); activeSerial.Close(); } return(true); } // void Transfer
/// <summary> /// The monitor recieved 0x00, 'p' ... /// Read the command ID bytes following that and process them. /// </summary> public static bool ReadCommand() { Console.WriteLine("Got PCDRV ..."); // Wait till we get the PCDrv function code while (serial.BytesToRead == 0) { } PCDrvCodes funcCode = (PCDrvCodes)TransferLogic.read32(); Console.WriteLine("Got function code: " + funcCode); // TODO: split these off into discrete functions? // PCInit if (funcCode == PCDrvCodes.PCINIT_101) { serial.Write("OKAY"); serial.Write(new byte[] { 0 }, 0, 1); return(true); } // PCCreat if (funcCode == PCDrvCodes.PCCREAT_102) { // tell unirom to start with the filename, etc serial.Write("OKAY"); string fileName = GetFilename(); if (fileName == "") { return(false); } UInt32 parameters = TransferLogic.read32(); bool isDir = ((parameters & 16) != 0); Console.WriteLine($"PCCreat( {fileName}, {parameters} )"); PCFile pcFile = GetOpenFile(fileName); if (pcFile != null) { // We're already tracking this file, just return it's handle Console.WriteLine("File already open, handle=" + pcFile.handle); serial.Write("OKAY"); serial.Write(BitConverter.GetBytes(pcFile.handle), 0, 4); return(true); } FileStream fStream; try { if (isDir) { throw new Exception("Directories are not supported!"); } else { if (!File.Exists(fileName)) { FileStream tempStream = File.Create(fileName); tempStream.Flush(); tempStream.Close(); tempStream.Dispose(); } else { Console.WriteLine($"File {fileName} already exists, using that..."); } fStream = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); } // open it read/write until otherwise specified via PCOpen UInt32 handle = NextHandle(); TrackFile(fileName, handle, fStream, PCFileMode.READWRITE); serial.Write("OKAY"); serial.Write(BitConverter.GetBytes(handle), 0, 4); return(true); } catch (Exception e) { Console.WriteLine($"Error creating file '{fileName}', ex={e}"); serial.Write("NOPE"); return(false); } } // PCCREAT_102 // PCOpen if (funcCode == PCDrvCodes.PCOPEN_103) { serial.Write("OKAY"); string fileName = GetFilename(); if (fileName == "") { return(false); } PCFileMode fileModeParams = (PCFileMode)TransferLogic.read32(); Console.WriteLine($"PCOpen( {fileName}, {fileModeParams} )"); PCFile f = GetOpenFile(fileName); if (f != null) { // just return the handle for this file... if (f.fileMode != fileModeParams) { Console.WriteLine($"File {f.handle} already open, switching params to {fileModeParams}"); f.fileMode = fileModeParams; } else { Console.WriteLine("File already open, handle=" + f.handle); } serial.Write("OKAY"); serial.Write(BitConverter.GetBytes(f.handle), 0, 4); return(true); } if (!File.Exists(fileName)) { Console.WriteLine("File doesn't exist!"); goto nope; } FileStream fs = null; try{ fs = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); } catch (Exception e) { Console.WriteLine($"Error opening file '{fileName}', ex={e}"); goto nope; } UInt32 handle = NextHandle(); TrackFile(fileName, handle, fs, fileModeParams); Console.WriteLine("Returning file, handle=" + handle); serial.Write("OKAY"); serial.Write(BitConverter.GetBytes(handle), 0, 4); return(true); nope :; Console.WriteLine("Failed.."); serial.Write("NOPE"); return(false); } // PCOPEN_103 // PCClose if (funcCode == PCDrvCodes.PCCLOSE_104) { serial.Write("OKAY"); // unirom sends extra params to save kernel // space by grouping similar commands together. UInt32 handle = TransferLogic.read32(); UInt32 unused1 = TransferLogic.read32(); UInt32 unused2 = TransferLogic.read32(); Console.WriteLine($"PCClose( {handle} ) unusedParams: {unused1},{unused2}"); PCFile f = GetOpenFile(handle); try{ if (f == null) { Console.WriteLine("No such file... great success!"); serial.Write("OKAY"); // v0 serial.Write(BitConverter.GetBytes(0), 0, 4); // v1 return(true); } else { Console.WriteLine($"Closing file {f.fileName} with handle {f.handle}..."); serial.Write("OKAY"); // v0 serial.Write(BitConverter.GetBytes(f.handle), 0, 4); // v1 // let the garbage collector deal with it ClosePCFile(f.handle); return(true); } } catch (Exception e) { Console.WriteLine("Error closing file..." + e); serial.Write("NOPE"); // v0 // don't need to send v1 return(false); } } // PCCLOSE_104 // PCRead if (funcCode == PCDrvCodes.PCREAD_105) { serial.Write("OKAY"); // unirom sends extra params to save kernel // space by grouping similar commands together, // so 'memaddr' is for debugging only. // PCRead() takes (handle, buff*, len ) // but interally passes it to _SN_Read as // ( 0, handle, len, buff* ), essentially // shuffling regs A0,A1,A2 into A1,A3,A2. // or just ( handle, len, buff* ). lol. UInt32 handle = TransferLogic.read32(); int inLength = (int)TransferLogic.read32(); UInt32 memaddr = TransferLogic.read32(); // not used, debugging only //Console.WriteLine( $"PCRead( {handle}, len={inLength}, dbg=0x{memaddr.ToString("X")} )" ); PCFile pcFile = GetOpenFile(handle); Console.WriteLine($"PCRead( {handle}, len=0x{inLength.ToString("X")} ); MemAddr=0x{memaddr.ToString("X")}, File={pcFile}"); if (pcFile == null) { Console.WriteLine($"No file with handle 0x{handle.ToString("X")}, returning!"); serial.Write("NOPE"); // v0 // don't need to send v1 return(false); } else { Console.WriteLine("Reading file " + pcFile.fileName); } long streamLength = 0; try{ FileStream fs = pcFile.fileStream; streamLength = fs.Length; byte[] bytes = new byte[inLength]; int bytesRead = fs.Read(bytes, 0, inLength); if (bytesRead <= 0) { //throw new Exception( "Read 0 bytes from the file.." ); Console.WriteLine("Warning - no bytes were read from the file - returning zeros..."); } // if we returned fewer bytes than requested, no biggie, the byte array is already set serial.Write("OKAY"); // v0 serial.Write(BitConverter.GetBytes(bytes.Length), 0, 4); // v1 // Then UInt32 check = TransferLogic.CalculateChecksum(bytes, false); serial.Write(BitConverter.GetBytes(check), 0, 4); TransferLogic.WriteBytes(bytes, false, true); // again the weird order reflects a desire to save space within the psx kernel // and reuse some functions } catch (Exception e) { Console.WriteLine($"Error reading file {pcFile.fileName} at pos={pcFile.fileStream.Position}, streamLength={streamLength} e={e}"); serial.Write("NOPE"); return(false); } } // PCREAD_105 // PCWrite if (funcCode == PCDrvCodes.PCWRITE_106) { serial.Write("OKAY"); // PCWrite() takes (handle, buff*, len ) // but interally passes it to _SN_Write as // ( 0, handle, len, buff* ), essentially // shuffling regs A0,A1,A2 into A1,A3,A2. // or just ( handle, len, buff* ). lol. UInt32 handle = TransferLogic.read32(); int inLength = (int)TransferLogic.read32(); UInt32 memaddr = TransferLogic.read32(); // not used, debugging only PCFile pcFile = GetOpenFile(handle); if (pcFile == null) { Console.WriteLine($"No file with handle 0x{handle.ToString("X")}, returning!"); serial.Write("NOPE"); // v0 // don't need to send v1 return(false); } Console.WriteLine($"PCWrite( {handle}, len={inLength} ); fileName={pcFile.fileName} SourceAddr={memaddr.ToString( "X" )}, File={pcFile}"); if (pcFile.fileMode == PCFileMode.READONLY) { Console.WriteLine("Error: File is readonly!"); serial.Write("NOPE"); return(false); } serial.Write("OKAY"); try { FileStream fs = pcFile.fileStream; byte[] bytes = new byte[inLength]; bool didRead = TransferLogic.ReadBytes_Raw((UInt32)inLength, bytes); if (!didRead) { throw new Exception("there was an error reading the stream from the psx!"); } Console.WriteLine($"Read {inLength} bytes, flushing to {pcFile.fileName}..."); fs.Write(bytes, 0, inLength); fs.Flush(true); serial.Write("OKAY"); // v0 serial.Write(BitConverter.GetBytes(bytes.Length), 0, 4); // v1 // again the weird order reflects a desire to save space within the psx kernel // and reuse some functions } catch (Exception e) { Console.WriteLine($"Error writing file {pcFile.fileName}, streamLength={inLength} e={e}"); serial.Write("NOPE"); return(false); } return(true); } //PCWRITE 106 if (funcCode == PCDrvCodes.PCSEEK_107) { serial.Write("OKAY"); UInt32 handle = TransferLogic.read32(); int seekPos = (int)TransferLogic.read32(); SeekOrigin seekOrigin = (SeekOrigin)TransferLogic.read32(); PCFile pcFile = GetOpenFile(handle); string fileName = pcFile != null ? pcFile.fileName : ""; Console.WriteLine($"PCSeek file {handle} to {seekPos}, type={seekOrigin}, fileName={fileName}"); if (pcFile == null) { Console.WriteLine("Error: There is no file with handle 0x" + handle.ToString("X")); serial.Write("NOPE"); return(false); } // Let's actually open the file and seek it to see if we bump into any issues try { FileStream fs = pcFile.fileStream; //fs.Seek( pcFile.filePointer, pcFile.seekMode ); fs.Seek(seekPos, seekOrigin); Console.WriteLine("Seeked position " + fs.Position); serial.Write("OKAY"); serial.Write(BitConverter.GetBytes(fs.Position), 0, 4); } catch (System.Exception e) { Console.WriteLine($"Exception when seeking file {handle}, e={e}"); serial.Write("NOPE"); return(false); } } //PCSEEK_107 return(true); }