IPCCommandResult CWII_IPC_HLE_Device_usb_oh0_46d_a03::IOCtl(u32 CommandAddress)
{
	IPCCommandResult SendReply = GetNoReply();

	SIOCtlVBuffer CommandBuffer(CommandAddress);

	switch (CommandBuffer.Parameter)
	{
	case USBV0_IOCTL_DEVREMOVALHOOK:
		// Reply is sent when device is removed
		//SendReply = true;
		break;

	default:
		WARN_LOG(OSHLE, "%s - IOCtl:", GetDeviceName().c_str());
		WARN_LOG(OSHLE, "    Parameter: 0x%x", CommandBuffer.Parameter);
		WARN_LOG(OSHLE, "    NumberIn: 0x%08x", CommandBuffer.NumberInBuffer);
		WARN_LOG(OSHLE, "    NumberOut: 0x%08x", CommandBuffer.NumberPayloadBuffer);
		WARN_LOG(OSHLE, "    BufferVector: 0x%08x", CommandBuffer.BufferVector);
		DumpAsync(CommandBuffer.BufferVector, CommandBuffer.NumberInBuffer, CommandBuffer.NumberPayloadBuffer);
		break;
	}

	Memory::Write_U32(0, CommandAddress + 4);
	return SendReply;
}
Exemple #2
0
IPCCommandResult CWII_IPC_HLE_Device_sdio_slot0::IOCtlV(u32 _CommandAddress)
{
	// PPC sending commands

	SIOCtlVBuffer CommandBuffer(_CommandAddress);

	// Prepare the out buffer(s) with zeros as a safety precaution
	// to avoid returning bad values
	for (const auto& buffer : CommandBuffer.PayloadBuffer)
		Memory::Memset(buffer.m_Address, 0, buffer.m_Size);

	u32 ReturnValue = 0;
	switch (CommandBuffer.Parameter)
	{
	case IOCTLV_SENDCMD:
		DEBUG_LOG(WII_IPC_SD, "IOCTLV_SENDCMD 0x%08x",
			Memory::Read_U32(CommandBuffer.InBuffer[0].m_Address));
		ReturnValue = ExecuteCommand(
			CommandBuffer.InBuffer[0].m_Address, CommandBuffer.InBuffer[0].m_Size,
			CommandBuffer.InBuffer[1].m_Address, CommandBuffer.InBuffer[1].m_Size,
			CommandBuffer.PayloadBuffer[0].m_Address, CommandBuffer.PayloadBuffer[0].m_Size);
		break;

	default:
		ERROR_LOG(WII_IPC_SD, "Unknown SD IOCtlV command 0x%08x", CommandBuffer.Parameter);
		break;
	}

	// DumpAsync(CommandBuffer.BufferVector, CommandBuffer.NumberInBuffer,
	// CommandBuffer.NumberPayloadBuffer, LogTypes::WII_IPC_SD);

	Memory::Write_U32(ReturnValue, _CommandAddress + 0x4);

	return GetDefaultReply();
}
IPCCommandResult CWII_IPC_HLE_Device_usb_oh0::IOCtlV(u32 CommandAddress)
{
	IPCCommandResult SendReply = GetNoReply();

	SIOCtlVBuffer CommandBuffer(CommandAddress);

	switch (CommandBuffer.Parameter)
	{
	case USBV0_IOCTL_DEVINSERTHOOKID:
	case USBV0_IOCTL_DEVINSERTHOOK:
		{
			u16 vid = Memory::Read_U16(CommandBuffer.InBuffer[0].m_Address);
			u16 pid = Memory::Read_U16(CommandBuffer.InBuffer[1].m_Address);
			WARN_LOG(OSHLE, "DEVINSERTHOOK %x/%x", vid, pid);
			// It is inserted
			SendReply = GetDefaultReply();
		}
		break;

	default:
		WARN_LOG(OSHLE, "%s - IOCtlV:", GetDeviceName().c_str());
		WARN_LOG(OSHLE, "    Parameter: 0x%x", CommandBuffer.Parameter);
		WARN_LOG(OSHLE, "    NumberIn: 0x%08x", CommandBuffer.NumberInBuffer);
		WARN_LOG(OSHLE, "    NumberOut: 0x%08x", CommandBuffer.NumberPayloadBuffer);
		WARN_LOG(OSHLE, "    BufferVector: 0x%08x", CommandBuffer.BufferVector);
		DumpAsync(CommandBuffer.BufferVector, CommandBuffer.NumberInBuffer,
			CommandBuffer.NumberPayloadBuffer, LogTypes::OSHLE, LogTypes::LWARNING);
		break;
	}

	Memory::Write_U32(0, CommandAddress + 4);
	return SendReply;
}
IPCCommandResult CWII_IPC_HLE_Device_usb_ven::IOCtlV(u32 CommandAddress)
{
	IPCCommandResult SendReply = GetNoReply();

	SIOCtlVBuffer CommandBuffer(CommandAddress);

	/*switch (CommandBuffer.Parameter)
	{
	default:
		WARN_LOG(OSHLE, "%s - IOCtlV:", GetDeviceName().c_str());
		WARN_LOG(OSHLE, "    Parameter: 0x%x", CommandBuffer.Parameter);
		WARN_LOG(OSHLE, "    NumberIn: 0x%08x", CommandBuffer.NumberInBuffer);
		WARN_LOG(OSHLE, "    NumberOut: 0x%08x", CommandBuffer.NumberPayloadBuffer);
		WARN_LOG(OSHLE, "    BufferVector: 0x%08x", CommandBuffer.BufferVector);
		DumpAsync(CommandBuffer.BufferVector, CommandBuffer.NumberInBuffer, CommandBuffer.NumberPayloadBuffer);
		break;
	}*/

	WARN_LOG(OSHLE, "%s - IOCtlV:", GetDeviceName().c_str());
	WARN_LOG(OSHLE, "    Parameter: 0x%x", CommandBuffer.Parameter);
	WARN_LOG(OSHLE, "    NumberIn: 0x%08x", CommandBuffer.NumberInBuffer);
	WARN_LOG(OSHLE, "    NumberOut: 0x%08x", CommandBuffer.NumberPayloadBuffer);
	WARN_LOG(OSHLE, "    BufferVector: 0x%08x", CommandBuffer.BufferVector);
	DumpAsync(CommandBuffer.BufferVector, CommandBuffer.NumberInBuffer, CommandBuffer.NumberPayloadBuffer);

	Memory::Write_U32(0, CommandAddress + 4);
	return SendReply;
}
Exemple #5
0
void Clean_Exit(int val)
{ char *command;

  command = CommandBuffer("","",SORT_PATH);
  sprintf(command,"rm -r %s",SORT_PATH);
  if (system(command) != 0)
    { fprintf(stderr,"%s: Command Failed:\n%*s      %s\n",
                     Prog_Name,(int) strlen(Prog_Name),"",command);
      exit (1);
    }
  exit (val);
}
Exemple #6
0
IPCCommandResult CWII_IPC_HLE_Device_usb_ven::IOCtlV(u32 _CommandAddress)
{
  SIOCtlVBuffer CommandBuffer(_CommandAddress);

  INFO_LOG(OSHLE, "%s - IOCtlV:", GetDeviceName().c_str());
  INFO_LOG(OSHLE, "  Parameter: 0x%x", CommandBuffer.Parameter);
  INFO_LOG(OSHLE, "  NumberIn: 0x%08x", CommandBuffer.NumberInBuffer);
  INFO_LOG(OSHLE, "  NumberOut: 0x%08x", CommandBuffer.NumberPayloadBuffer);
  INFO_LOG(OSHLE, "  BufferVector: 0x%08x", CommandBuffer.BufferVector);
  DumpAsync(CommandBuffer.BufferVector, CommandBuffer.NumberInBuffer,
            CommandBuffer.NumberPayloadBuffer);

  Memory::Write_U32(0, _CommandAddress + 4);
  return GetNoReply();
}
IPCCommandResult CWII_IPC_HLE_Device_di::IOCtlV(u32 _CommandAddress)
{
	SIOCtlVBuffer CommandBuffer(_CommandAddress);

	// Prepare the out buffer(s) with zeros as a safety precaution
	// to avoid returning bad values
	for (u32 i = 0; i < CommandBuffer.NumberPayloadBuffer; i++)
	{
		Memory::Memset(CommandBuffer.PayloadBuffer[i].m_Address, 0,
			CommandBuffer.PayloadBuffer[i].m_Size);
	}

	u32 ReturnValue = 0;
	switch (CommandBuffer.Parameter)
	{
	case DVDInterface::DVDLowOpenPartition:
		{
			_dbg_assert_msg_(WII_IPC_DVD, CommandBuffer.InBuffer[1].m_Address == 0, "DVDLowOpenPartition with ticket");
			_dbg_assert_msg_(WII_IPC_DVD, CommandBuffer.InBuffer[2].m_Address == 0, "DVDLowOpenPartition with cert chain");

			u64 const partition_offset = ((u64)Memory::Read_U32(CommandBuffer.InBuffer[0].m_Address + 4) << 2);
			DVDInterface::ChangePartition(partition_offset);

			INFO_LOG(WII_IPC_DVD, "DVDLowOpenPartition: partition_offset 0x%016" PRIx64, partition_offset);

			// Read TMD to the buffer
			u32 tmd_size;
			std::unique_ptr<u8[]> tmd_buf = DVDInterface::GetVolume().GetTMD(&tmd_size);
			Memory::CopyToEmu(CommandBuffer.PayloadBuffer[0].m_Address, tmd_buf.get(), tmd_size);
			WII_IPC_HLE_Interface::ES_DIVerify(tmd_buf.get(), tmd_size);

			ReturnValue = 1;
		}
		break;

	default:
		ERROR_LOG(WII_IPC_DVD, "IOCtlV: %i", CommandBuffer.Parameter);
		_dbg_assert_msg_(WII_IPC_DVD, 0, "IOCtlV: %i", CommandBuffer.Parameter);
		break;
	}

	Memory::Write_U32(ReturnValue, _CommandAddress + 4);
	return GetDefaultReply();
}
Exemple #8
0
bool CWII_IPC_HLE_Device_hid::IOCtlV(u32 _CommandAddress)
{

	Dolphin_Debugger::PrintCallstack(LogTypes::WII_IPC_HID, LogTypes::LWARNING);
	u32 ReturnValue = 0;
	SIOCtlVBuffer CommandBuffer(_CommandAddress);

	DEBUG_LOG(WII_IPC_HID, "%s - IOCtlV:", GetDeviceName().c_str());
	DEBUG_LOG(WII_IPC_HID, "    Parameter: 0x%x", CommandBuffer.Parameter);
	DEBUG_LOG(WII_IPC_HID, "    NumberIn: 0x%08x", CommandBuffer.NumberInBuffer);
	DEBUG_LOG(WII_IPC_HID, "    NumberOut: 0x%08x", CommandBuffer.NumberPayloadBuffer);
	DEBUG_LOG(WII_IPC_HID, "    BufferVector: 0x%08x", CommandBuffer.BufferVector);
	DEBUG_LOG(WII_IPC_HID, "    PayloadAddr: 0x%08x", CommandBuffer.PayloadBuffer[0].m_Address);
	DEBUG_LOG(WII_IPC_HID, "    PayloadSize: 0x%08x", CommandBuffer.PayloadBuffer[0].m_Size);
	#if defined(_DEBUG) || defined(DEBUGFAST)
	DumpAsync(CommandBuffer.BufferVector, CommandBuffer.NumberInBuffer, CommandBuffer.NumberPayloadBuffer);
	#endif

	Memory::Write_U32(ReturnValue, _CommandAddress + 4);
	return true;
}
bool CWII_IPC_HLE_Device_fs::IOCtlV(u32 _CommandAddress)
{
	u32 ReturnValue = FS_RESULT_OK;
	SIOCtlVBuffer CommandBuffer(_CommandAddress);

	// Prepare the out buffer(s) with zeros as a safety precaution
	// to avoid returning bad values
	for(u32 i = 0; i < CommandBuffer.NumberPayloadBuffer; i++)
	{
		Memory::Memset(CommandBuffer.PayloadBuffer[i].m_Address, 0,
			CommandBuffer.PayloadBuffer[i].m_Size);
	}

	switch(CommandBuffer.Parameter)
	{
	case IOCTLV_READ_DIR:
		{
			// the wii uses this function to define the type (dir or file)
			std::string DirName(HLE_IPC_BuildFilename((const char*)Memory::GetPointer(
				CommandBuffer.InBuffer[0].m_Address), CommandBuffer.InBuffer[0].m_Size));

			INFO_LOG(WII_IPC_FILEIO, "FS: IOCTL_READ_DIR %s", DirName.c_str());

			if (!File::Exists(DirName))
			{
				WARN_LOG(WII_IPC_FILEIO, "FS: Search not found: %s", DirName.c_str());
				ReturnValue = FS_FILE_NOT_EXIST;
				break;
			}
			else if (!File::IsDirectory(DirName))
			{
				// It's not a directory, so error.
				// Games don't usually seem to care WHICH error they get, as long as it's <
				// Well the system menu CARES!
				WARN_LOG(WII_IPC_FILEIO, "\tNot a directory - return FS_RESULT_FATAL");
				ReturnValue = FS_RESULT_FATAL;
				break;
			}

			// make a file search
			CFileSearch::XStringVector Directories;
			Directories.push_back(DirName);

			CFileSearch::XStringVector Extensions;
			Extensions.push_back("*.*");

			CFileSearch FileSearch(Extensions, Directories);

			// it is one
			if ((CommandBuffer.InBuffer.size() == 1) && (CommandBuffer.PayloadBuffer.size() == 1))
			{
				size_t numFile = FileSearch.GetFileNames().size();
				INFO_LOG(WII_IPC_FILEIO, "\t%lu files found", (unsigned long)numFile);

				Memory::Write_U32((u32)numFile, CommandBuffer.PayloadBuffer[0].m_Address);
			}
			else
			{
				u32 MaxEntries = Memory::Read_U32(CommandBuffer.InBuffer[0].m_Address);

				memset(Memory::GetPointer(CommandBuffer.PayloadBuffer[0].m_Address), 0, CommandBuffer.PayloadBuffer[0].m_Size);

				size_t numFiles = 0;
				char* pFilename = (char*)Memory::GetPointer((u32)(CommandBuffer.PayloadBuffer[0].m_Address));

				for (size_t i=0; i<FileSearch.GetFileNames().size(); i++)
				{
					if (i >= MaxEntries)
						break;

					std::string name, ext;
					SplitPath(FileSearch.GetFileNames()[i], NULL, &name, &ext);
					std::string FileName = name + ext;

					// Decode entities of invalid file system characters so that
					// games (such as HP:HBP) will be able to find what they expect.
					for (const Common::replace_t& r : replacements)
					{
						for (size_t j = 0; (j = FileName.find(r.second, j)) != FileName.npos; ++j)
							FileName.replace(j, r.second.length(), 1, r.first);
					}

					strcpy(pFilename, FileName.c_str());
					pFilename += FileName.length();
					*pFilename++ = 0x00;  // termination
					numFiles++;

					INFO_LOG(WII_IPC_FILEIO, "\tFound: %s", FileName.c_str());
				}

				Memory::Write_U32((u32)numFiles, CommandBuffer.PayloadBuffer[1].m_Address);
			}

			ReturnValue = FS_RESULT_OK;
		}
		break;

	case IOCTLV_GETUSAGE:
		{
			_dbg_assert_(WII_IPC_FILEIO, CommandBuffer.PayloadBuffer.size() == 2);
			_dbg_assert_(WII_IPC_FILEIO, CommandBuffer.PayloadBuffer[0].m_Size == 4);
			_dbg_assert_(WII_IPC_FILEIO, CommandBuffer.PayloadBuffer[1].m_Size == 4);

			// this command sucks because it asks of the number of used
			// fsBlocks and inodes
			// It should be correct, but don't count on it...
			const char *relativepath = (const char*)Memory::GetPointer(CommandBuffer.InBuffer[0].m_Address);
			std::string path(HLE_IPC_BuildFilename(relativepath, CommandBuffer.InBuffer[0].m_Size));
			u32 fsBlocks = 0;
			u32 iNodes = 0;

			INFO_LOG(WII_IPC_FILEIO, "IOCTL_GETUSAGE %s", path.c_str());
			if (File::IsDirectory(path))
			{
				// LPFaint99: After I found that setting the number of inodes to the number of children + 1 for the directory itself
				// I decided to compare with sneek which has the following 2 special cases which are
				// Copyright (C) 2009-2011  crediar http://code.google.com/p/sneek/
				if ((memcmp(relativepath, "/title/00010001", 16 ) == 0 ) ||
					(memcmp(relativepath, "/title/00010005", 16) == 0 ))
				{
					fsBlocks = 23; // size is size/0x4000
					iNodes = 42; // empty folders return a FileCount of 1
				}
				else
				{
					File::FSTEntry parentDir;
					// add one for the folder itself, allows some games to create their save files
					// R8XE52 (Jurassic: The Hunted), STEETR (Tetris Party Deluxe) now create their saves with this change
					iNodes = 1 + File::ScanDirectoryTree(path, parentDir);

					u64 totalSize = ComputeTotalFileSize(parentDir); // "Real" size, to be converted to nand blocks

					fsBlocks = (u32)(totalSize / (16 * 1024));  // one bock is 16kb
				}
				ReturnValue = FS_RESULT_OK;

				INFO_LOG(WII_IPC_FILEIO, "FS: fsBlock: %i, iNodes: %i", fsBlocks, iNodes);
			}
			else
			{
				fsBlocks = 0;
				iNodes = 0;
				ReturnValue = FS_RESULT_OK;
				WARN_LOG(WII_IPC_FILEIO, "FS: fsBlock failed, cannot find directory: %s", path.c_str());
			}

			Memory::Write_U32(fsBlocks, CommandBuffer.PayloadBuffer[0].m_Address);
			Memory::Write_U32(iNodes, CommandBuffer.PayloadBuffer[1].m_Address);
		}
		break;


	default:
		PanicAlert("CWII_IPC_HLE_Device_fs::IOCtlV: %i", CommandBuffer.Parameter);
		break;
	}

	Memory::Write_U32(ReturnValue, _CommandAddress+4);

	return true;
}
Exemple #10
0
void WiiSocket::Update(bool read, bool write, bool except)
{
	auto it = pending_sockops.begin();
	while (it != pending_sockops.end())
	{
		s32 ReturnValue = 0;
		bool forceNonBlock = false;
		IPCCommandType ct = static_cast<IPCCommandType>(Memory::Read_U32(it->_CommandAddress));
		if (!it->is_ssl && ct == IPC_CMD_IOCTL)
		{
			u32 BufferIn = Memory::Read_U32(it->_CommandAddress + 0x10);
			u32 BufferInSize = Memory::Read_U32(it->_CommandAddress + 0x14);
			u32 BufferOut = Memory::Read_U32(it->_CommandAddress + 0x18);
			u32 BufferOutSize = Memory::Read_U32(it->_CommandAddress + 0x1C);

			switch (it->net_type)
			{
			case IOCTL_SO_FCNTL:
			{
				u32 cmd = Memory::Read_U32(BufferIn + 4);
				u32 arg = Memory::Read_U32(BufferIn + 8);
				ReturnValue = FCntl(cmd, arg);
				break;
			}
			case IOCTL_SO_BIND:
			{
				//u32 has_addr = Memory::Read_U32(BufferIn + 0x04);
				sockaddr_in local_name;
				WiiSockAddrIn* wii_name = (WiiSockAddrIn*)Memory::GetPointer(BufferIn + 0x08);
				WiiSockMan::Convert(*wii_name, local_name);

				int ret = bind(fd, (sockaddr*)&local_name, sizeof(local_name));
				ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_BIND", false);

				INFO_LOG(WII_IPC_NET, "IOCTL_SO_BIND (%08X %s:%d) = %d ", fd,
					inet_ntoa(local_name.sin_addr), Common::swap16(local_name.sin_port), ret);
				break;
			}
			case IOCTL_SO_CONNECT:
			{
				//u32 has_addr = Memory::Read_U32(BufferIn + 0x04);
				sockaddr_in local_name;
				WiiSockAddrIn* wii_name = (WiiSockAddrIn*)Memory::GetPointer(BufferIn + 0x08);
				WiiSockMan::Convert(*wii_name, local_name);

				int ret = connect(fd, (sockaddr*)&local_name, sizeof(local_name));
				ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_CONNECT", false);

				INFO_LOG(WII_IPC_NET,"IOCTL_SO_CONNECT (%08x, %s:%d)",
					fd, inet_ntoa(local_name.sin_addr), Common::swap16(local_name.sin_port));
				break;
			}
			case IOCTL_SO_ACCEPT:
			{
				if (BufferOutSize > 0)
				{
					sockaddr_in local_name;
					WiiSockAddrIn* wii_name = (WiiSockAddrIn*)Memory::GetPointer(BufferOut);
					WiiSockMan::Convert(*wii_name, local_name);

					socklen_t addrlen = sizeof(sockaddr_in);
					int ret = (s32)accept(fd, (sockaddr*)&local_name, &addrlen);
					ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_ACCEPT", true);

					WiiSockMan::Convert(local_name, *wii_name, addrlen);
				}
				else
				{
					int ret = (s32)accept(fd, nullptr, nullptr);
					ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_ACCEPT", true);
				}

				WiiSockMan::GetInstance().AddSocket(ReturnValue);

				INFO_LOG(WII_IPC_NET, "IOCTL_SO_ACCEPT "
					"BufferIn: (%08x, %i), BufferOut: (%08x, %i)",
					BufferIn, BufferInSize, BufferOut, BufferOutSize);

				break;
			}
			default:
				break;
			}

			// Fix blocking error codes
			if (!nonBlock)
			{
				if (it->net_type == IOCTL_SO_CONNECT &&
				    ReturnValue == -SO_EISCONN)
				{
					ReturnValue = SO_SUCCESS;
				}
			}
		}
		else if (ct == IPC_CMD_IOCTLV)
		{
			SIOCtlVBuffer CommandBuffer(it->_CommandAddress);
			u32 BufferIn = 0, BufferIn2 = 0;
			u32 BufferInSize = 0, BufferInSize2 = 0;
			u32 BufferOut = 0, BufferOut2 = 0;
			u32 BufferOutSize = 0, BufferOutSize2 = 0;

			if (CommandBuffer.InBuffer.size() > 0)
			{
				BufferIn = CommandBuffer.InBuffer.at(0).m_Address;
				BufferInSize = CommandBuffer.InBuffer.at(0).m_Size;
			}

			if (CommandBuffer.PayloadBuffer.size() > 0)
			{
				BufferOut = CommandBuffer.PayloadBuffer.at(0).m_Address;
				BufferOutSize = CommandBuffer.PayloadBuffer.at(0).m_Size;
			}

			if (CommandBuffer.PayloadBuffer.size() > 1)
			{
				BufferOut2 = CommandBuffer.PayloadBuffer.at(1).m_Address;
				BufferOutSize2 = CommandBuffer.PayloadBuffer.at(1).m_Size;
			}

			if (CommandBuffer.InBuffer.size() > 1)
			{
				BufferIn2 = CommandBuffer.InBuffer.at(1).m_Address;
				BufferInSize2 = CommandBuffer.InBuffer.at(1).m_Size;
			}

			if (it->is_ssl)
			{
				int sslID = Memory::Read_U32(BufferOut) - 1;
				if (SSLID_VALID(sslID))
				{
					switch (it->ssl_type)
					{
					case IOCTLV_NET_SSL_DOHANDSHAKE:
					{

						int ret = mbedtls_ssl_handshake(&CWII_IPC_HLE_Device_net_ssl::_SSL[sslID].ctx);
						switch (ret)
						{
						case 0:
							Memory::Write_U32(SSL_OK, BufferIn);
							break;
						case MBEDTLS_ERR_SSL_WANT_READ:
							Memory::Write_U32(SSL_ERR_RAGAIN, BufferIn);
							if (!nonBlock)
								ReturnValue = SSL_ERR_RAGAIN;
							break;
						case MBEDTLS_ERR_SSL_WANT_WRITE:
							Memory::Write_U32(SSL_ERR_WAGAIN, BufferIn);
							if (!nonBlock)
								ReturnValue = SSL_ERR_WAGAIN;
							break;
						default:
							Memory::Write_U32(SSL_ERR_FAILED, BufferIn);
							break;
						}

						INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_DOHANDSHAKE = (%d) "
							"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
							"BufferOut: (%08x, %i), BufferOut2: (%08x, %i)",
							ret,
							BufferIn, BufferInSize, BufferIn2, BufferInSize2,
							BufferOut, BufferOutSize, BufferOut2, BufferOutSize2);
						break;
					}
					case IOCTLV_NET_SSL_WRITE:
					{
						int ret = mbedtls_ssl_write(&CWII_IPC_HLE_Device_net_ssl::_SSL[sslID].ctx, Memory::GetPointer(BufferOut2), BufferOutSize2);

#ifdef DEBUG_SSL
						File::IOFile("ssl_write.bin", "ab").WriteBytes(Memory::GetPointer(BufferOut2), BufferOutSize2);
#endif
						if (ret >= 0)
						{
							// Return bytes written or SSL_ERR_ZERO if none
							Memory::Write_U32((ret == 0) ? SSL_ERR_ZERO : ret, BufferIn);
						}
						else
						{
							switch (ret)
							{
							case MBEDTLS_ERR_SSL_WANT_READ:
								Memory::Write_U32(SSL_ERR_RAGAIN, BufferIn);
								if (!nonBlock)
									ReturnValue = SSL_ERR_RAGAIN;
								break;
							case MBEDTLS_ERR_SSL_WANT_WRITE:
								Memory::Write_U32(SSL_ERR_WAGAIN, BufferIn);
								if (!nonBlock)
									ReturnValue = SSL_ERR_WAGAIN;
								break;
							default:
								Memory::Write_U32(SSL_ERR_FAILED, BufferIn);
								break;
							}
						}
						break;
					}
					case IOCTLV_NET_SSL_READ:
					{
						int ret = mbedtls_ssl_read(&CWII_IPC_HLE_Device_net_ssl::_SSL[sslID].ctx, Memory::GetPointer(BufferIn2), BufferInSize2);
#ifdef DEBUG_SSL
						if (ret > 0)
						{
							File::IOFile("ssl_read.bin", "ab").WriteBytes(Memory::GetPointer(BufferIn2), ret);
						}
#endif
						if (ret >= 0)
						{
							// Return bytes read or SSL_ERR_ZERO if none
							Memory::Write_U32((ret == 0) ? SSL_ERR_ZERO : ret, BufferIn);
						}
						else
						{
							switch (ret)
							{
							case MBEDTLS_ERR_SSL_WANT_READ:
								Memory::Write_U32(SSL_ERR_RAGAIN, BufferIn);
								if (!nonBlock)
									ReturnValue = SSL_ERR_RAGAIN;
								break;
							case MBEDTLS_ERR_SSL_WANT_WRITE:
								Memory::Write_U32(SSL_ERR_WAGAIN, BufferIn);
								if (!nonBlock)
									ReturnValue = SSL_ERR_WAGAIN;
								break;
							default:
								Memory::Write_U32(SSL_ERR_FAILED, BufferIn);
								break;
							}
						}
						break;
					}
					default:
						break;
					}
				}
				else
				{
					Memory::Write_U32(SSL_ERR_ID, BufferIn);
				}
			}
			else
			{
				switch (it->net_type)
				{
				case IOCTLV_SO_SENDTO:
				{

					u32 flags = Memory::Read_U32(BufferIn2 + 0x04);
					u32 has_destaddr = Memory::Read_U32(BufferIn2 + 0x08);

                                        // Not a string, Windows requires a const char* for sendto
					const char* data = (const char*)Memory::GetPointer(BufferIn);

					// Act as non blocking when SO_MSG_NONBLOCK is specified
					forceNonBlock = ((flags & SO_MSG_NONBLOCK) == SO_MSG_NONBLOCK);
					// send/sendto only handles MSG_OOB
					flags &= SO_MSG_OOB;

					sockaddr_in local_name = {0};
					if (has_destaddr)
					{
						WiiSockAddrIn* wii_name = (WiiSockAddrIn*)Memory::GetPointer(BufferIn2 + 0x0C);
						WiiSockMan::Convert(*wii_name, local_name);
					}

					int ret = sendto(fd, data, BufferInSize, flags,
						has_destaddr ? (struct sockaddr*)&local_name : nullptr,
						has_destaddr ? sizeof(sockaddr) :  0);
					ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_SENDTO", true);

					INFO_LOG(WII_IPC_NET,
						"%s = %d Socket: %08x, BufferIn: (%08x, %i), BufferIn2: (%08x, %i), %u.%u.%u.%u",
						has_destaddr ? "IOCTLV_SO_SENDTO " : "IOCTLV_SO_SEND ",
						ReturnValue, fd, BufferIn, BufferInSize,
						BufferIn2, BufferInSize2,
						local_name.sin_addr.s_addr & 0xFF,
						(local_name.sin_addr.s_addr >> 8) & 0xFF,
						(local_name.sin_addr.s_addr >> 16) & 0xFF,
						(local_name.sin_addr.s_addr >> 24) & 0xFF
						);
					break;
				}
				case IOCTLV_SO_RECVFROM:
				{
					u32 flags = Memory::Read_U32(BufferIn + 0x04);
					// Not a string, Windows requires a char* for recvfrom
					char* data = (char*)Memory::GetPointer(BufferOut);
					int data_len = BufferOutSize;

					sockaddr_in local_name;
					memset(&local_name, 0, sizeof(sockaddr_in));

					if (BufferOutSize2 != 0)
					{
						WiiSockAddrIn* wii_name = (WiiSockAddrIn*)Memory::GetPointer(BufferOut2);
						WiiSockMan::Convert(*wii_name, local_name);
					}

					// Act as non blocking when SO_MSG_NONBLOCK is specified
					forceNonBlock = ((flags & SO_MSG_NONBLOCK) == SO_MSG_NONBLOCK);

					// recv/recvfrom only handles PEEK/OOB
					flags &= SO_MSG_PEEK | SO_MSG_OOB;
#ifdef _WIN32
					if (flags & SO_MSG_PEEK)
					{
						unsigned long totallen = 0;
						ioctlsocket(fd, FIONREAD, &totallen);
						ReturnValue = totallen;
						break;
					}
#endif
					socklen_t addrlen = sizeof(sockaddr_in);
					int ret = recvfrom(fd, data, data_len, flags,
									BufferOutSize2 ? (struct sockaddr*) &local_name : nullptr,
									BufferOutSize2 ? &addrlen : nullptr);
					ReturnValue = WiiSockMan::GetNetErrorCode(ret, BufferOutSize2 ? "SO_RECVFROM" : "SO_RECV", true);

					INFO_LOG(WII_IPC_NET, "%s(%d, %p) Socket: %08X, Flags: %08X, "
					"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
					"BufferOut: (%08x, %i), BufferOut2: (%08x, %i)",
					BufferOutSize2 ? "IOCTLV_SO_RECVFROM " : "IOCTLV_SO_RECV ",
					ReturnValue, data, fd, flags,
					BufferIn, BufferInSize, BufferIn2, BufferInSize2,
					BufferOut, BufferOutSize, BufferOut2, BufferOutSize2);

					if (BufferOutSize2 != 0)
					{
						WiiSockAddrIn* wii_name = (WiiSockAddrIn*)Memory::GetPointer(BufferOut2);
						WiiSockMan::Convert(local_name, *wii_name, addrlen);
					}
					break;
				}
				default:
					break;
				}
			}
		}

		if (nonBlock ||
			forceNonBlock ||
		    (!it->is_ssl &&
		     ReturnValue != -SO_EAGAIN &&
		     ReturnValue != -SO_EINPROGRESS &&
		     ReturnValue != -SO_EALREADY) ||
		    (it->is_ssl &&
		     ReturnValue != SSL_ERR_WAGAIN &&
		     ReturnValue != SSL_ERR_RAGAIN))
		{
			DEBUG_LOG(WII_IPC_NET,
			          "IOCTL(V) Sock: %08x ioctl/v: %d returned: %d nonBlock: %d forceNonBlock: %d",
			          fd, it->is_ssl ? (int) it->ssl_type : (int) it->net_type, ReturnValue, nonBlock, forceNonBlock);
			WiiSockMan::EnqueueReply(it->_CommandAddress, ReturnValue, ct);
			it = pending_sockops.erase(it);
		}
		else
		{
			++it;
		}
	}
IPCCommandResult CWII_IPC_HLE_Device_net_ssl::IOCtlV(u32 _CommandAddress)
{
	SIOCtlVBuffer CommandBuffer(_CommandAddress);

	u32 _BufferIn = 0, _BufferIn2 = 0, _BufferIn3 = 0;
	u32 BufferInSize = 0, BufferInSize2 = 0, BufferInSize3 = 0;

	u32 BufferOut = 0, BufferOut2 = 0, BufferOut3 = 0;
	u32 BufferOutSize = 0, BufferOutSize2 = 0, BufferOutSize3 = 0;

	if (CommandBuffer.InBuffer.size() > 0)
	{
		_BufferIn = CommandBuffer.InBuffer.at(0).m_Address;
		BufferInSize = CommandBuffer.InBuffer.at(0).m_Size;
	}
	if (CommandBuffer.InBuffer.size() > 1)
	{
		_BufferIn2 = CommandBuffer.InBuffer.at(1).m_Address;
		BufferInSize2 = CommandBuffer.InBuffer.at(1).m_Size;
	}
	if (CommandBuffer.InBuffer.size() > 2)
	{
		_BufferIn3 = CommandBuffer.InBuffer.at(2).m_Address;
		BufferInSize3 = CommandBuffer.InBuffer.at(2).m_Size;
	}

	if (CommandBuffer.PayloadBuffer.size() > 0)
	{
		BufferOut = CommandBuffer.PayloadBuffer.at(0).m_Address;
		BufferOutSize = CommandBuffer.PayloadBuffer.at(0).m_Size;
	}
	if (CommandBuffer.PayloadBuffer.size() > 1)
	{
		BufferOut2 = CommandBuffer.PayloadBuffer.at(1).m_Address;
		BufferOutSize2 = CommandBuffer.PayloadBuffer.at(1).m_Size;
	}
	if (CommandBuffer.PayloadBuffer.size() > 2)
	{
		BufferOut3 = CommandBuffer.PayloadBuffer.at(2).m_Address;
		BufferOutSize3 = CommandBuffer.PayloadBuffer.at(2).m_Size;
	}

	// I don't trust SSL to be deterministic, and this is never going to sync
	// as such (as opposed to forwarding IPC results or whatever), so -
	if (Core::g_want_determinism)
	{
		Memory::Write_U32(-1, _CommandAddress + 0x4);
		return IPC_DEFAULT_REPLY;
	}

	switch (CommandBuffer.Parameter)
	{
	case IOCTLV_NET_SSL_NEW:
	{
		int verifyOption = Memory::Read_U32(BufferOut);
		std::string hostname = Memory::GetString(BufferOut2, BufferOutSize2);

		int freeSSL = GetSSLFreeID();
		if (freeSSL)
		{
			int sslID = freeSSL - 1;
			WII_SSL* ssl = &_SSL[sslID];
			int ret = ssl_init(&ssl->ctx);
			if (ret)
			{
				goto _SSL_NEW_ERROR;
			}

			entropy_init(&ssl->entropy);
			const char* pers = "dolphin-emu";
			ret = ctr_drbg_init(&ssl->ctr_drbg, entropy_func,
			                    &ssl->entropy,
			                    (const unsigned char*)pers,
			                    strlen(pers));
			if (ret)
			{
				ssl_free(&ssl->ctx);
				entropy_free(&ssl->entropy);
				goto _SSL_NEW_ERROR;
			}

			ssl_set_rng(&ssl->ctx, ctr_drbg_random, &ssl->ctr_drbg);

			// For some reason we can't use TLSv1.2, v1.1 and below are fine!
			ssl_set_max_version(&ssl->ctx, SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_2);

			ssl_set_session(&ssl->ctx, &ssl->session);

			ssl_set_endpoint(&ssl->ctx, SSL_IS_CLIENT);
			ssl_set_authmode(&ssl->ctx, SSL_VERIFY_NONE);
			ssl_set_renegotiation(&ssl->ctx, SSL_RENEGOTIATION_ENABLED);

			ssl->hostname = hostname;
			ssl_set_hostname(&ssl->ctx, ssl->hostname.c_str());

			ssl->active = true;
			Memory::Write_U32(freeSSL, _BufferIn);
		}
		else
		{
_SSL_NEW_ERROR:
			Memory::Write_U32(SSL_ERR_FAILED, _BufferIn);
		}

		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_NEW (%d, %s) "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			verifyOption, hostname.c_str(),
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);
		break;
	}
	case IOCTLV_NET_SSL_SHUTDOWN:
	{
		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			WII_SSL* ssl = &_SSL[sslID];
			ssl_close_notify(&ssl->ctx);
			ssl_session_free(&ssl->session);
			ssl_free(&ssl->ctx);

			entropy_free(&ssl->entropy);

			x509_crt_free(&ssl->cacert);
			x509_crt_free(&ssl->clicert);

			ssl->hostname.clear();

			ssl->active = false;

			Memory::Write_U32(SSL_OK, _BufferIn);
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
		}
		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SHUTDOWN "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);
		break;
	}
	case IOCTLV_NET_SSL_SETROOTCA:
	{
		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SETROOTCA "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);


		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			WII_SSL* ssl = &_SSL[sslID];
			int ret = x509_crt_parse_der(
				&ssl->cacert,
				Memory::GetPointer(BufferOut2),
				BufferOutSize2);

			if (ret)
			{
				Memory::Write_U32(SSL_ERR_FAILED, _BufferIn);
			}
			else
			{
				ssl_set_ca_chain(&ssl->ctx, &ssl->cacert, nullptr, ssl->hostname.c_str());
				Memory::Write_U32(SSL_OK, _BufferIn);
			}

			INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SETROOTCA = %d", ret);
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
		}
		break;
	}
	case IOCTLV_NET_SSL_SETBUILTINCLIENTCERT:
	{
		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SETBUILTINCLIENTCERT "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);

		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			WII_SSL* ssl = &_SSL[sslID];
			std::string cert_base_path = File::GetUserPath(D_SESSION_WIIROOT_IDX);
			int ret = x509_crt_parse_file(&ssl->clicert, (cert_base_path + "clientca.pem").c_str());
			int pk_ret = pk_parse_keyfile(&ssl->pk, (cert_base_path + "clientcakey.pem").c_str(), nullptr);
			if (ret || pk_ret)
			{
				x509_crt_free(&ssl->clicert);
				pk_free(&ssl->pk);
				Memory::Write_U32(SSL_ERR_FAILED, _BufferIn);
			}
			else
			{
				ssl_set_own_cert(&ssl->ctx, &ssl->clicert, &ssl->pk);
				Memory::Write_U32(SSL_OK, _BufferIn);
			}

			INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SETBUILTINCLIENTCERT = (%d, %d)", ret, pk_ret);
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
			INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SETBUILTINCLIENTCERT invalid sslID = %d", sslID);
		}
		break;
	}
	case IOCTLV_NET_SSL_REMOVECLIENTCERT:
	{
		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_REMOVECLIENTCERT "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);

		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			WII_SSL* ssl = &_SSL[sslID];
			x509_crt_free(&ssl->clicert);
			pk_free(&ssl->pk);

			ssl_set_own_cert(&ssl->ctx, nullptr, nullptr);
			Memory::Write_U32(SSL_OK, _BufferIn);
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
			INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SETBUILTINCLIENTCERT invalid sslID = %d", sslID);
		}
		break;
	}
	case IOCTLV_NET_SSL_SETBUILTINROOTCA:
	{
		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			WII_SSL* ssl = &_SSL[sslID];

			int ret = x509_crt_parse_file(&ssl->cacert, (File::GetUserPath(D_SESSION_WIIROOT_IDX) + "/rootca.pem").c_str());
			if (ret)
			{
				x509_crt_free(&ssl->clicert);
				Memory::Write_U32(SSL_ERR_FAILED, _BufferIn);
			}
			else
			{
				ssl_set_ca_chain(&ssl->ctx, &ssl->cacert, nullptr, ssl->hostname.c_str());
				Memory::Write_U32(SSL_OK, _BufferIn);
			}
			INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SETBUILTINROOTCA = %d", ret);
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
		}
		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SETBUILTINROOTCA "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);
		break;
	}
	case IOCTLV_NET_SSL_CONNECT:
	{
		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			WII_SSL* ssl = &_SSL[sslID];
			ssl->sockfd = Memory::Read_U32(BufferOut2);
			INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_CONNECT socket = %d", ssl->sockfd);
			ssl_set_bio(&ssl->ctx, net_recv, &ssl->sockfd, net_send, &ssl->sockfd);
			Memory::Write_U32(SSL_OK, _BufferIn);
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
		}
		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_CONNECT "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);
		break;
	}
	case IOCTLV_NET_SSL_DOHANDSHAKE:
	{
		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			WiiSockMan &sm = WiiSockMan::GetInstance();
			sm.DoSock(_SSL[sslID].sockfd, _CommandAddress, IOCTLV_NET_SSL_DOHANDSHAKE);
			return IPC_NO_REPLY;
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
		}
		break;
	}
	case IOCTLV_NET_SSL_WRITE:
	{
		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			WiiSockMan &sm = WiiSockMan::GetInstance();
			sm.DoSock(_SSL[sslID].sockfd, _CommandAddress, IOCTLV_NET_SSL_WRITE);
			return IPC_NO_REPLY;
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
		}
		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_WRITE "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);
		INFO_LOG(WII_IPC_SSL, "%s", Memory::GetString(BufferOut2).c_str());
		break;
	}
	case IOCTLV_NET_SSL_READ:
	{

		int ret = 0;
		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			WiiSockMan &sm = WiiSockMan::GetInstance();
			sm.DoSock(_SSL[sslID].sockfd, _CommandAddress, IOCTLV_NET_SSL_READ);
			return IPC_NO_REPLY;
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
		}

		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_READ(%d)"
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			ret,
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);
		break;
	}
	case IOCTLV_NET_SSL_SETROOTCADEFAULT:
	{
		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			Memory::Write_U32(SSL_OK, _BufferIn);
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
		}
		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SETROOTCADEFAULT "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);
		break;
	}
	case IOCTLV_NET_SSL_SETCLIENTCERTDEFAULT:
	{
		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SETCLIENTCERTDEFAULT "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);

		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			Memory::Write_U32(SSL_OK, _BufferIn);
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
		}
		break;
	}
	default:
		ERROR_LOG(WII_IPC_SSL, "%i "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			CommandBuffer.Parameter,
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);
		break;
	}

	// SSL return codes are written to BufferIn
	Memory::Write_U32(0, _CommandAddress+4);

	return IPC_DEFAULT_REPLY;
}
IPCCommandResult CWII_IPC_HLE_Device_fs::IOCtlV(u32 _CommandAddress)
{
  u32 ReturnValue = FS_RESULT_OK;
  SIOCtlVBuffer CommandBuffer(_CommandAddress);

  // Prepare the out buffer(s) with zeros as a safety precaution
  // to avoid returning bad values
  for (u32 i = 0; i < CommandBuffer.NumberPayloadBuffer; i++)
  {
    Memory::Memset(CommandBuffer.PayloadBuffer[i].m_Address, 0,
                   CommandBuffer.PayloadBuffer[i].m_Size);
  }

  switch (CommandBuffer.Parameter)
  {
  case IOCTLV_READ_DIR:
  {
    const std::string relative_path =
        Memory::GetString(CommandBuffer.InBuffer[0].m_Address, CommandBuffer.InBuffer[0].m_Size);

    if (!IsValidWiiPath(relative_path))
    {
      WARN_LOG(WII_IPC_FILEIO, "Not a valid path: %s", relative_path.c_str());
      ReturnValue = FS_RESULT_FATAL;
      break;
    }

    // the Wii uses this function to define the type (dir or file)
    std::string DirName(HLE_IPC_BuildFilename(relative_path));

    INFO_LOG(WII_IPC_FILEIO, "FS: IOCTL_READ_DIR %s", DirName.c_str());

    if (!File::Exists(DirName))
    {
      WARN_LOG(WII_IPC_FILEIO, "FS: Search not found: %s", DirName.c_str());
      ReturnValue = FS_FILE_NOT_EXIST;
      break;
    }
    else if (!File::IsDirectory(DirName))
    {
      // It's not a directory, so error.
      // Games don't usually seem to care WHICH error they get, as long as it's <
      // Well the system menu CARES!
      WARN_LOG(WII_IPC_FILEIO, "\tNot a directory - return FS_RESULT_FATAL");
      ReturnValue = FS_RESULT_FATAL;
      break;
    }

    File::FSTEntry entry = File::ScanDirectoryTree(DirName, false);

    // it is one
    if ((CommandBuffer.InBuffer.size() == 1) && (CommandBuffer.PayloadBuffer.size() == 1))
    {
      size_t numFile = entry.children.size();
      INFO_LOG(WII_IPC_FILEIO, "\t%zu files found", numFile);

      Memory::Write_U32((u32)numFile, CommandBuffer.PayloadBuffer[0].m_Address);
    }
    else
    {
      for (File::FSTEntry& child : entry.children)
      {
        // Decode escaped invalid file system characters so that games (such as
        // Harry Potter and the Half-Blood Prince) can find what they expect.
        child.virtualName = Common::UnescapeFileName(child.virtualName);
      }

      std::sort(entry.children.begin(), entry.children.end(),
                [](const File::FSTEntry& one, const File::FSTEntry& two) {
                  return one.virtualName < two.virtualName;
                });

      u32 MaxEntries = Memory::Read_U32(CommandBuffer.InBuffer[0].m_Address);

      memset(Memory::GetPointer(CommandBuffer.PayloadBuffer[0].m_Address), 0,
             CommandBuffer.PayloadBuffer[0].m_Size);

      size_t numFiles = 0;
      char* pFilename = (char*)Memory::GetPointer((u32)(CommandBuffer.PayloadBuffer[0].m_Address));

      for (size_t i = 0; i < entry.children.size() && i < MaxEntries; i++)
      {
        const std::string& FileName = entry.children[i].virtualName;

        strcpy(pFilename, FileName.c_str());
        pFilename += FileName.length();
        *pFilename++ = 0x00;  // termination
        numFiles++;

        INFO_LOG(WII_IPC_FILEIO, "\tFound: %s", FileName.c_str());
      }

      Memory::Write_U32((u32)numFiles, CommandBuffer.PayloadBuffer[1].m_Address);
    }

    ReturnValue = FS_RESULT_OK;
  }
  break;

  case IOCTLV_GETUSAGE:
  {
    _dbg_assert_(WII_IPC_FILEIO, CommandBuffer.PayloadBuffer.size() == 2);
    _dbg_assert_(WII_IPC_FILEIO, CommandBuffer.PayloadBuffer[0].m_Size == 4);
    _dbg_assert_(WII_IPC_FILEIO, CommandBuffer.PayloadBuffer[1].m_Size == 4);

    // this command sucks because it asks of the number of used
    // fsBlocks and inodes
    // It should be correct, but don't count on it...
    std::string relativepath =
        Memory::GetString(CommandBuffer.InBuffer[0].m_Address, CommandBuffer.InBuffer[0].m_Size);

    if (!IsValidWiiPath(relativepath))
    {
      WARN_LOG(WII_IPC_FILEIO, "Not a valid path: %s", relativepath.c_str());
      ReturnValue = FS_RESULT_FATAL;
      break;
    }

    std::string path(HLE_IPC_BuildFilename(relativepath));
    u32 fsBlocks = 0;
    u32 iNodes = 0;

    INFO_LOG(WII_IPC_FILEIO, "IOCTL_GETUSAGE %s", path.c_str());
    if (File::IsDirectory(path))
    {
      // LPFaint99: After I found that setting the number of inodes to the number of children + 1
      // for the directory itself
      // I decided to compare with sneek which has the following 2 special cases which are
      // Copyright (C) 2009-2011  crediar http://code.google.com/p/sneek/
      if ((relativepath.compare(0, 16, "/title/00010001") == 0) ||
          (relativepath.compare(0, 16, "/title/00010005") == 0))
      {
        fsBlocks = 23;  // size is size/0x4000
        iNodes = 42;    // empty folders return a FileCount of 1
      }
      else
      {
        File::FSTEntry parentDir = File::ScanDirectoryTree(path, true);
        // add one for the folder itself
        iNodes = 1 + (u32)parentDir.size;

        u64 totalSize =
            ComputeTotalFileSize(parentDir);  // "Real" size, to be converted to nand blocks

        fsBlocks = (u32)(totalSize / (16 * 1024));  // one bock is 16kb
      }
      ReturnValue = FS_RESULT_OK;

      INFO_LOG(WII_IPC_FILEIO, "FS: fsBlock: %i, iNodes: %i", fsBlocks, iNodes);
    }
    else
    {
      fsBlocks = 0;
      iNodes = 0;
      ReturnValue = FS_RESULT_OK;
      WARN_LOG(WII_IPC_FILEIO, "FS: fsBlock failed, cannot find directory: %s", path.c_str());
    }

    Memory::Write_U32(fsBlocks, CommandBuffer.PayloadBuffer[0].m_Address);
    Memory::Write_U32(iNodes, CommandBuffer.PayloadBuffer[1].m_Address);
  }
  break;

  default:
    PanicAlert("CWII_IPC_HLE_Device_fs::IOCtlV: %i", CommandBuffer.Parameter);
    break;
  }

  Memory::Write_U32(ReturnValue, _CommandAddress + 4);

  return GetFSReply();
}
bool CWII_IPC_HLE_Device_net_ssl::IOCtlV(u32 _CommandAddress)
{
	SIOCtlVBuffer CommandBuffer(_CommandAddress);

	u32 _BufferIn = 0, _BufferIn2 = 0, _BufferIn3 = 0;
	u32 BufferInSize = 0, BufferInSize2 = 0, BufferInSize3 = 0;

	u32 BufferOut = 0, BufferOut2 = 0, BufferOut3 = 0;
	u32 BufferOutSize = 0, BufferOutSize2 = 0, BufferOutSize3 = 0;

	if (CommandBuffer.InBuffer.size() > 0)
	{
		_BufferIn = CommandBuffer.InBuffer.at(0).m_Address;
		BufferInSize = CommandBuffer.InBuffer.at(0).m_Size;
	}
	if (CommandBuffer.InBuffer.size() > 1)
	{
		_BufferIn2 = CommandBuffer.InBuffer.at(1).m_Address;
		BufferInSize2 = CommandBuffer.InBuffer.at(1).m_Size;
	}
	if (CommandBuffer.InBuffer.size() > 2)
	{
		_BufferIn3 = CommandBuffer.InBuffer.at(2).m_Address;
		BufferInSize3 = CommandBuffer.InBuffer.at(2).m_Size;
	}

	if (CommandBuffer.PayloadBuffer.size() > 0)
	{
		BufferOut = CommandBuffer.PayloadBuffer.at(0).m_Address;
		BufferOutSize = CommandBuffer.PayloadBuffer.at(0).m_Size;
	}
	if (CommandBuffer.PayloadBuffer.size() > 1)
	{
		BufferOut2 = CommandBuffer.PayloadBuffer.at(1).m_Address;
		BufferOutSize2 = CommandBuffer.PayloadBuffer.at(1).m_Size;
	}
	if (CommandBuffer.PayloadBuffer.size() > 2)
	{
		BufferOut3 = CommandBuffer.PayloadBuffer.at(2).m_Address;
		BufferOutSize3 = CommandBuffer.PayloadBuffer.at(2).m_Size;
	}

	switch (CommandBuffer.Parameter)
	{
	case IOCTLV_NET_SSL_NEW:
	{
		int verifyOption = Memory::Read_U32(BufferOut);
		const char * hostname = (const char*) Memory::GetPointer(BufferOut2);

		int freeSSL = this->getSSLFreeID();
		if (freeSSL)
		{
			int sslID = freeSSL - 1;
			int ret = ssl_init(&_SSL[sslID].ctx);
			if (ret)
			{
				// Cleanup possibly dirty ctx
				memset(&_SSL[sslID].ctx, 0, sizeof(ssl_context));
				goto _SSL_NEW_ERROR;
			}

			entropy_init(&_SSL[sslID].entropy);
			const char* pers = "dolphin-emu";
			ret = ctr_drbg_init(&_SSL[sslID].ctr_drbg, entropy_func,
			                    &_SSL[sslID].entropy,
			                    (const unsigned char*)pers,
			                    strlen(pers));
			if (ret)
			{
				ssl_free(&_SSL[sslID].ctx);
				// Cleanup possibly dirty ctx
				memset(&_SSL[sslID].ctx, 0, sizeof(ssl_context));
				entropy_free(&_SSL[sslID].entropy);
				goto _SSL_NEW_ERROR;
			}

			ssl_set_rng(&_SSL[sslID].ctx, ctr_drbg_random, &_SSL[sslID].ctr_drbg);

			// For some reason we can't use TLSv1.2, v1.1 and below are fine!
			ssl_set_max_version(&_SSL[sslID].ctx, SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_2);

			ssl_set_session(&_SSL[sslID].ctx, &_SSL[sslID].session);

			ssl_set_endpoint(&_SSL[sslID].ctx, SSL_IS_CLIENT);
			ssl_set_authmode(&_SSL[sslID].ctx, SSL_VERIFY_NONE);
			ssl_set_renegotiation(&_SSL[sslID].ctx, SSL_RENEGOTIATION_ENABLED);

			memcpy(_SSL[sslID].hostname, hostname, min((int)BufferOutSize2, NET_SSL_MAX_HOSTNAME_LEN));
			_SSL[sslID].hostname[NET_SSL_MAX_HOSTNAME_LEN-1] = '\0';
			ssl_set_hostname(&_SSL[sslID].ctx, _SSL[sslID].hostname);

			_SSL[sslID].active = true;
			Memory::Write_U32(freeSSL, _BufferIn);
		}
		else
		{
_SSL_NEW_ERROR:
			Memory::Write_U32(SSL_ERR_FAILED, _BufferIn);
		}

		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_NEW (%d, %s) "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			verifyOption, hostname,
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);
		break;
	}
	case IOCTLV_NET_SSL_SHUTDOWN:
	{
		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			ssl_close_notify(&_SSL[sslID].ctx);
			ssl_session_free(&_SSL[sslID].session);
			ssl_free(&_SSL[sslID].ctx);

			entropy_free(&_SSL[sslID].entropy);
			
			x509_crt_free(&_SSL[sslID].cacert);
			x509_crt_free(&_SSL[sslID].clicert);

			memset(&_SSL[sslID].ctx, 0, sizeof(ssl_context));
			memset(&_SSL[sslID].session, 0, sizeof(ssl_session));
			memset(&_SSL[sslID].entropy, 0, sizeof(entropy_context));
			memset(_SSL[sslID].hostname, 0, NET_SSL_MAX_HOSTNAME_LEN);

			_SSL[sslID].active = false;

			Memory::Write_U32(SSL_OK, _BufferIn);
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
		}
		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SHUTDOWN "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);
		break;
	}
	case IOCTLV_NET_SSL_SETROOTCA:
	{
		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SETROOTCA "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);


		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			int ret = x509_crt_parse_der(
				&_SSL[sslID].cacert,
				Memory::GetPointer(BufferOut2),
				BufferOutSize2);

			if (ret)
			{
				Memory::Write_U32(SSL_ERR_FAILED, _BufferIn);
			}
			else
			{
				ssl_set_ca_chain(&_SSL[sslID].ctx, &_SSL[sslID].cacert, NULL, _SSL[sslID].hostname);
				Memory::Write_U32(SSL_OK, _BufferIn);
			}

			INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SETROOTCA = %d", ret);
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
		}
		break;
	}
	case IOCTLV_NET_SSL_SETBUILTINCLIENTCERT:
	{
		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SETBUILTINCLIENTCERT "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);

		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			std::string cert_base_path(File::GetUserPath(D_WIIUSER_IDX));
			int ret = x509_crt_parse_file(&_SSL[sslID].clicert, (cert_base_path + "clientca.pem").c_str());
			int pk_ret = pk_parse_keyfile(&_SSL[sslID].pk, (cert_base_path + "clientcakey.pem").c_str(), NULL);
			if (ret || pk_ret)
			{
				x509_crt_free(&_SSL[sslID].clicert);
				pk_free(&_SSL[sslID].pk);
				memset(&_SSL[sslID].clicert, 0, sizeof(x509_crt));
				memset(&_SSL[sslID].pk, 0, sizeof(pk_context));
				Memory::Write_U32(SSL_ERR_FAILED, _BufferIn);
			}
			else
			{
				ssl_set_own_cert(&_SSL[sslID].ctx, &_SSL[sslID].clicert, &_SSL[sslID].pk);
				Memory::Write_U32(SSL_OK, _BufferIn);
			}

			INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SETBUILTINCLIENTCERT = (%d, %d)", ret, pk_ret);
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
			INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SETBUILTINCLIENTCERT invalid sslID = %d", sslID);
		}
		break;
	}
	case IOCTLV_NET_SSL_REMOVECLIENTCERT:
	{
		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_REMOVECLIENTCERT "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);

		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			x509_crt_free(&_SSL[sslID].clicert);
			pk_free(&_SSL[sslID].pk);
			memset(&_SSL[sslID].clicert, 0, sizeof(x509_crt));
			memset(&_SSL[sslID].pk, 0, sizeof(pk_context));

			ssl_set_own_cert(&_SSL[sslID].ctx, NULL, NULL);
			Memory::Write_U32(SSL_OK, _BufferIn);
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
			INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SETBUILTINCLIENTCERT invalid sslID = %d", sslID);
		}
		break;
	}
	case IOCTLV_NET_SSL_SETBUILTINROOTCA:
	{
		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			std::string cert_base_path(File::GetUserPath(D_WIIUSER_IDX));

			int ret = x509_crt_parse_file(&_SSL[sslID].cacert, (cert_base_path + "rootca.pem").c_str());
			if (ret)
			{
				x509_crt_free(&_SSL[sslID].clicert);
				Memory::Write_U32(SSL_ERR_FAILED, _BufferIn);
			}
			else
			{
				ssl_set_ca_chain(&_SSL[sslID].ctx, &_SSL[sslID].cacert, NULL, _SSL[sslID].hostname);
				Memory::Write_U32(SSL_OK, _BufferIn);
			}
			INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SETBUILTINROOTCA = %d", ret);
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
		}
		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SETBUILTINROOTCA "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);
		break;
	}
	case IOCTLV_NET_SSL_CONNECT:
	{
		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			_SSL[sslID].sockfd = Memory::Read_U32(BufferOut2);
			INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_CONNECT socket = %d", _SSL[sslID].sockfd);
			ssl_set_bio(&_SSL[sslID].ctx, net_recv, &_SSL[sslID].sockfd, net_send, &_SSL[sslID].sockfd);
			Memory::Write_U32(SSL_OK, _BufferIn);
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
		}
		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_CONNECT "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);
		break;
	}
	case IOCTLV_NET_SSL_DOHANDSHAKE:
	{
		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			WiiSockMan &sm = WiiSockMan::getInstance();
			sm.doSock(_SSL[sslID].sockfd, _CommandAddress, IOCTLV_NET_SSL_DOHANDSHAKE);
			return false;
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
		}
		break;
	}
	case IOCTLV_NET_SSL_WRITE:
	{
		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			WiiSockMan &sm = WiiSockMan::getInstance();
			sm.doSock(_SSL[sslID].sockfd, _CommandAddress, IOCTLV_NET_SSL_WRITE);
			return false;
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
		}
		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_WRITE "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);
		INFO_LOG(WII_IPC_SSL, "%s", Memory::GetPointer(BufferOut2));
		break;
	}
	case IOCTLV_NET_SSL_READ:
	{

		int ret = 0;
		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			WiiSockMan &sm = WiiSockMan::getInstance();
			sm.doSock(_SSL[sslID].sockfd, _CommandAddress, IOCTLV_NET_SSL_READ);
			return false;
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
		}

		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_READ(%d)"
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			ret,
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);
		break;
	}
	case IOCTLV_NET_SSL_SETROOTCADEFAULT:
	{
		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			Memory::Write_U32(SSL_OK, _BufferIn);
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
		}
		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SETROOTCADEFAULT "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);
		break;
	}
	case IOCTLV_NET_SSL_SETCLIENTCERTDEFAULT:
	{
		INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_SETCLIENTCERTDEFAULT "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);

		int sslID = Memory::Read_U32(BufferOut) - 1;
		if (SSLID_VALID(sslID))
		{
			Memory::Write_U32(SSL_OK, _BufferIn);
		}
		else
		{
			Memory::Write_U32(SSL_ERR_ID, _BufferIn);
		}
		break;
	}
	default:
		ERROR_LOG(WII_IPC_SSL, "%i "
			"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
			"BufferIn3: (%08x, %i), BufferOut: (%08x, %i), "
			"BufferOut2: (%08x, %i), BufferOut3: (%08x, %i)",
			CommandBuffer.Parameter,
			_BufferIn, BufferInSize, _BufferIn2, BufferInSize2,
			_BufferIn3, BufferInSize3, BufferOut, BufferOutSize,
			BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);
		break;
	}

	// SSL return codes are written to BufferIn
	Memory::Write_U32(0, _CommandAddress+4);

	return true;
}
Exemple #14
0
void WiiSocket::Update(bool read, bool write, bool except)
{
  auto it = pending_sockops.begin();
  while (it != pending_sockops.end())
  {
    s32 ReturnValue = 0;
    bool forceNonBlock = false;
    IPCCommandType ct = static_cast<IPCCommandType>(Memory::Read_U32(it->_CommandAddress));
    if (!it->is_ssl && ct == IPC_CMD_IOCTL)
    {
      u32 BufferIn = Memory::Read_U32(it->_CommandAddress + 0x10);
      u32 BufferInSize = Memory::Read_U32(it->_CommandAddress + 0x14);
      u32 BufferOut = Memory::Read_U32(it->_CommandAddress + 0x18);
      u32 BufferOutSize = Memory::Read_U32(it->_CommandAddress + 0x1C);

      switch (it->net_type)
      {
      case IOCTL_SO_FCNTL:
      {
        u32 cmd = Memory::Read_U32(BufferIn + 4);
        u32 arg = Memory::Read_U32(BufferIn + 8);
        ReturnValue = FCntl(cmd, arg);
        break;
      }
      case IOCTL_SO_BIND:
      {
        // u32 has_addr = Memory::Read_U32(BufferIn + 0x04);
        sockaddr_in local_name;
        WiiSockAddrIn* wii_name = (WiiSockAddrIn*)Memory::GetPointer(BufferIn + 0x08);
        WiiSockMan::Convert(*wii_name, local_name);

        int ret = bind(fd, (sockaddr*)&local_name, sizeof(local_name));
        ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_BIND", false);

        INFO_LOG(WII_IPC_NET, "IOCTL_SO_BIND (%08X %s:%d) = %d ", fd,
                 inet_ntoa(local_name.sin_addr), Common::swap16(local_name.sin_port), ret);
        break;
      }
      case IOCTL_SO_CONNECT:
      {
        // u32 has_addr = Memory::Read_U32(BufferIn + 0x04);
        sockaddr_in local_name;
        WiiSockAddrIn* wii_name = (WiiSockAddrIn*)Memory::GetPointer(BufferIn + 0x08);
        WiiSockMan::Convert(*wii_name, local_name);

        int ret = connect(fd, (sockaddr*)&local_name, sizeof(local_name));
        ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_CONNECT", false);

        INFO_LOG(WII_IPC_NET, "IOCTL_SO_CONNECT (%08x, %s:%d)", fd, inet_ntoa(local_name.sin_addr),
                 Common::swap16(local_name.sin_port));
        break;
      }
      case IOCTL_SO_ACCEPT:
      {
        if (BufferOutSize > 0)
        {
          sockaddr_in local_name;
          WiiSockAddrIn* wii_name = (WiiSockAddrIn*)Memory::GetPointer(BufferOut);
          WiiSockMan::Convert(*wii_name, local_name);

          socklen_t addrlen = sizeof(sockaddr_in);
          int ret = (s32)accept(fd, (sockaddr*)&local_name, &addrlen);
          ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_ACCEPT", true);

          WiiSockMan::Convert(local_name, *wii_name, addrlen);
        }
        else
        {
          int ret = (s32)accept(fd, nullptr, nullptr);
          ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_ACCEPT", true);
        }

        WiiSockMan::GetInstance().AddSocket(ReturnValue);

        INFO_LOG(WII_IPC_NET, "IOCTL_SO_ACCEPT "
                              "BufferIn: (%08x, %i), BufferOut: (%08x, %i)",
                 BufferIn, BufferInSize, BufferOut, BufferOutSize);

        break;
      }
      default:
        break;
      }

      // Fix blocking error codes
      if (!nonBlock)
      {
        if (it->net_type == IOCTL_SO_CONNECT && ReturnValue == -SO_EISCONN)
        {
          ReturnValue = SO_SUCCESS;
        }
      }
    }
    else if (ct == IPC_CMD_IOCTLV)
    {
      SIOCtlVBuffer CommandBuffer(it->_CommandAddress);
      u32 BufferIn = 0, BufferIn2 = 0;
      u32 BufferInSize = 0, BufferInSize2 = 0;
      u32 BufferOut = 0, BufferOut2 = 0;
      u32 BufferOutSize = 0, BufferOutSize2 = 0;

      if (CommandBuffer.InBuffer.size() > 0)
      {
        BufferIn = CommandBuffer.InBuffer.at(0).m_Address;
        BufferInSize = CommandBuffer.InBuffer.at(0).m_Size;
      }

      if (CommandBuffer.PayloadBuffer.size() > 0)
      {
        BufferOut = CommandBuffer.PayloadBuffer.at(0).m_Address;
        BufferOutSize = CommandBuffer.PayloadBuffer.at(0).m_Size;
      }

      if (CommandBuffer.PayloadBuffer.size() > 1)
      {
        BufferOut2 = CommandBuffer.PayloadBuffer.at(1).m_Address;
        BufferOutSize2 = CommandBuffer.PayloadBuffer.at(1).m_Size;
      }

      if (CommandBuffer.InBuffer.size() > 1)
      {
        BufferIn2 = CommandBuffer.InBuffer.at(1).m_Address;
        BufferInSize2 = CommandBuffer.InBuffer.at(1).m_Size;
      }

      if (it->is_ssl)
      {
        int sslID = Memory::Read_U32(BufferOut) - 1;
        if (SSLID_VALID(sslID))
        {
          switch (it->ssl_type)
          {
          case IOCTLV_NET_SSL_DOHANDSHAKE:
          {
            mbedtls_ssl_context* ctx = &CWII_IPC_HLE_Device_net_ssl::_SSL[sslID].ctx;
            int ret = mbedtls_ssl_handshake(ctx);
            if (ret)
            {
              char error_buffer[256] = "";
              mbedtls_strerror(ret, error_buffer, sizeof(error_buffer));
              ERROR_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_DOHANDSHAKE: %s", error_buffer);
            }
            switch (ret)
            {
            case 0:
              Memory::Write_U32(SSL_OK, BufferIn);
              break;
            case MBEDTLS_ERR_SSL_WANT_READ:
              Memory::Write_U32(SSL_ERR_RAGAIN, BufferIn);
              if (!nonBlock)
                ReturnValue = SSL_ERR_RAGAIN;
              break;
            case MBEDTLS_ERR_SSL_WANT_WRITE:
              Memory::Write_U32(SSL_ERR_WAGAIN, BufferIn);
              if (!nonBlock)
                ReturnValue = SSL_ERR_WAGAIN;
              break;
            case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED:
            {
              char error_buffer[256] = "";
              int res = mbedtls_ssl_get_verify_result(ctx);
              mbedtls_x509_crt_verify_info(error_buffer, sizeof(error_buffer), "", res);
              ERROR_LOG(WII_IPC_SSL, "MBEDTLS_ERR_X509_CERT_VERIFY_FAILED (verify_result = %d): %s",
                        res, error_buffer);

              if (res & MBEDTLS_X509_BADCERT_CN_MISMATCH)
                res = SSL_ERR_VCOMMONNAME;
              else if (res & MBEDTLS_X509_BADCERT_NOT_TRUSTED)
                res = SSL_ERR_VROOTCA;
              else if (res & MBEDTLS_X509_BADCERT_REVOKED)
                res = SSL_ERR_VCHAIN;
              else if (res & MBEDTLS_X509_BADCERT_EXPIRED || res & MBEDTLS_X509_BADCERT_FUTURE)
                res = SSL_ERR_VDATE;
              else
                res = SSL_ERR_FAILED;

              Memory::Write_U32(res, BufferIn);
              if (!nonBlock)
                ReturnValue = res;
              break;
            }
            default:
              Memory::Write_U32(SSL_ERR_FAILED, BufferIn);
              break;
            }

            // mbedtls_ssl_get_peer_cert(ctx) seems not to work if handshake failed
            // Below is an alternative to dump the peer certificate
            if (SConfig::GetInstance().m_SSLDumpPeerCert && ctx->session_negotiate != nullptr)
            {
              const mbedtls_x509_crt* cert = ctx->session_negotiate->peer_cert;
              if (cert != nullptr)
              {
                std::string filename = File::GetUserPath(D_DUMPSSL_IDX) +
                                       ((ctx->hostname != nullptr) ? ctx->hostname : "") +
                                       "_peercert.der";
                File::IOFile(filename, "wb").WriteBytes(cert->raw.p, cert->raw.len);
              }
            }

            INFO_LOG(WII_IPC_SSL, "IOCTLV_NET_SSL_DOHANDSHAKE = (%d) "
                                  "BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
                                  "BufferOut: (%08x, %i), BufferOut2: (%08x, %i)",
                     ret, BufferIn, BufferInSize, BufferIn2, BufferInSize2, BufferOut,
                     BufferOutSize, BufferOut2, BufferOutSize2);
            break;
          }
          case IOCTLV_NET_SSL_WRITE:
          {
            int ret = mbedtls_ssl_write(&CWII_IPC_HLE_Device_net_ssl::_SSL[sslID].ctx,
                                        Memory::GetPointer(BufferOut2), BufferOutSize2);

            if (SConfig::GetInstance().m_SSLDumpWrite && ret > 0)
            {
              std::string filename = File::GetUserPath(D_DUMPSSL_IDX) +
                                     SConfig::GetInstance().GetGameID() + "_write.bin";
              File::IOFile(filename, "ab").WriteBytes(Memory::GetPointer(BufferOut2), ret);
            }

            if (ret >= 0)
            {
              // Return bytes written or SSL_ERR_ZERO if none
              Memory::Write_U32((ret == 0) ? SSL_ERR_ZERO : ret, BufferIn);
            }
            else
            {
              switch (ret)
              {
              case MBEDTLS_ERR_SSL_WANT_READ:
                Memory::Write_U32(SSL_ERR_RAGAIN, BufferIn);
                if (!nonBlock)
                  ReturnValue = SSL_ERR_RAGAIN;
                break;
              case MBEDTLS_ERR_SSL_WANT_WRITE:
                Memory::Write_U32(SSL_ERR_WAGAIN, BufferIn);
                if (!nonBlock)
                  ReturnValue = SSL_ERR_WAGAIN;
                break;
              default:
                Memory::Write_U32(SSL_ERR_FAILED, BufferIn);
                break;
              }
            }
            break;
          }
          case IOCTLV_NET_SSL_READ:
          {
            int ret = mbedtls_ssl_read(&CWII_IPC_HLE_Device_net_ssl::_SSL[sslID].ctx,
                                       Memory::GetPointer(BufferIn2), BufferInSize2);

            if (SConfig::GetInstance().m_SSLDumpRead && ret > 0)
            {
              std::string filename = File::GetUserPath(D_DUMPSSL_IDX) +
                                     SConfig::GetInstance().GetGameID() + "_read.bin";
              File::IOFile(filename, "ab").WriteBytes(Memory::GetPointer(BufferIn2), ret);
            }

            if (ret >= 0)
            {
              // Return bytes read or SSL_ERR_ZERO if none
              Memory::Write_U32((ret == 0) ? SSL_ERR_ZERO : ret, BufferIn);
            }
            else
            {
              switch (ret)
              {
              case MBEDTLS_ERR_SSL_WANT_READ:
                Memory::Write_U32(SSL_ERR_RAGAIN, BufferIn);
                if (!nonBlock)
                  ReturnValue = SSL_ERR_RAGAIN;
                break;
              case MBEDTLS_ERR_SSL_WANT_WRITE:
                Memory::Write_U32(SSL_ERR_WAGAIN, BufferIn);
                if (!nonBlock)
                  ReturnValue = SSL_ERR_WAGAIN;
                break;
              default:
                Memory::Write_U32(SSL_ERR_FAILED, BufferIn);
                break;
              }
            }
            break;
          }
          default:
            break;
          }
        }
        else
        {
          Memory::Write_U32(SSL_ERR_ID, BufferIn);
        }
      }
      else
      {
        switch (it->net_type)
        {
        case IOCTLV_SO_SENDTO:
        {
          u32 flags = Memory::Read_U32(BufferIn2 + 0x04);
          u32 has_destaddr = Memory::Read_U32(BufferIn2 + 0x08);

          // Not a string, Windows requires a const char* for sendto
          const char* data = (const char*)Memory::GetPointer(BufferIn);

          // Act as non blocking when SO_MSG_NONBLOCK is specified
          forceNonBlock = ((flags & SO_MSG_NONBLOCK) == SO_MSG_NONBLOCK);
          // send/sendto only handles MSG_OOB
          flags &= SO_MSG_OOB;

          sockaddr_in local_name = {0};
          if (has_destaddr)
          {
            WiiSockAddrIn* wii_name = (WiiSockAddrIn*)Memory::GetPointer(BufferIn2 + 0x0C);
            WiiSockMan::Convert(*wii_name, local_name);
          }

          int ret = sendto(fd, data, BufferInSize, flags,
                           has_destaddr ? (struct sockaddr*)&local_name : nullptr,
                           has_destaddr ? sizeof(sockaddr) : 0);
          ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_SENDTO", true);

          DEBUG_LOG(
              WII_IPC_NET,
              "%s = %d Socket: %08x, BufferIn: (%08x, %i), BufferIn2: (%08x, %i), %u.%u.%u.%u",
              has_destaddr ? "IOCTLV_SO_SENDTO " : "IOCTLV_SO_SEND ", ReturnValue, fd, BufferIn,
              BufferInSize, BufferIn2, BufferInSize2, local_name.sin_addr.s_addr & 0xFF,
              (local_name.sin_addr.s_addr >> 8) & 0xFF, (local_name.sin_addr.s_addr >> 16) & 0xFF,
              (local_name.sin_addr.s_addr >> 24) & 0xFF);
          break;
        }
        case IOCTLV_SO_RECVFROM:
        {
          u32 flags = Memory::Read_U32(BufferIn + 0x04);
          // Not a string, Windows requires a char* for recvfrom
          char* data = (char*)Memory::GetPointer(BufferOut);
          int data_len = BufferOutSize;

          sockaddr_in local_name;
          memset(&local_name, 0, sizeof(sockaddr_in));

          if (BufferOutSize2 != 0)
          {
            WiiSockAddrIn* wii_name = (WiiSockAddrIn*)Memory::GetPointer(BufferOut2);
            WiiSockMan::Convert(*wii_name, local_name);
          }

          // Act as non blocking when SO_MSG_NONBLOCK is specified
          forceNonBlock = ((flags & SO_MSG_NONBLOCK) == SO_MSG_NONBLOCK);

          // recv/recvfrom only handles PEEK/OOB
          flags &= SO_MSG_PEEK | SO_MSG_OOB;
#ifdef _WIN32
          if (flags & SO_MSG_PEEK)
          {
            unsigned long totallen = 0;
            ioctlsocket(fd, FIONREAD, &totallen);
            ReturnValue = totallen;
            break;
          }
#endif
          socklen_t addrlen = sizeof(sockaddr_in);
          int ret = recvfrom(fd, data, data_len, flags,
                             BufferOutSize2 ? (struct sockaddr*)&local_name : nullptr,
                             BufferOutSize2 ? &addrlen : nullptr);
          ReturnValue =
              WiiSockMan::GetNetErrorCode(ret, BufferOutSize2 ? "SO_RECVFROM" : "SO_RECV", true);

          INFO_LOG(WII_IPC_NET, "%s(%d, %p) Socket: %08X, Flags: %08X, "
                                "BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
                                "BufferOut: (%08x, %i), BufferOut2: (%08x, %i)",
                   BufferOutSize2 ? "IOCTLV_SO_RECVFROM " : "IOCTLV_SO_RECV ", ReturnValue, data,
                   fd, flags, BufferIn, BufferInSize, BufferIn2, BufferInSize2, BufferOut,
                   BufferOutSize, BufferOut2, BufferOutSize2);

          if (BufferOutSize2 != 0)
          {
            WiiSockAddrIn* wii_name = (WiiSockAddrIn*)Memory::GetPointer(BufferOut2);
            WiiSockMan::Convert(local_name, *wii_name, addrlen);
          }
          break;
        }
        default:
          break;
        }
      }
    }

    if (nonBlock || forceNonBlock ||
        (!it->is_ssl && ReturnValue != -SO_EAGAIN && ReturnValue != -SO_EINPROGRESS &&
         ReturnValue != -SO_EALREADY) ||
        (it->is_ssl && ReturnValue != SSL_ERR_WAGAIN && ReturnValue != SSL_ERR_RAGAIN))
    {
      DEBUG_LOG(WII_IPC_NET,
                "IOCTL(V) Sock: %08x ioctl/v: %d returned: %d nonBlock: %d forceNonBlock: %d", fd,
                it->is_ssl ? (int)it->ssl_type : (int)it->net_type, ReturnValue, nonBlock,
                forceNonBlock);
      Memory::Write_U32(ReturnValue, it->_CommandAddress + 4);
      WII_IPC_HLE_Interface::EnqueueReply(it->_CommandAddress);
      it = pending_sockops.erase(it);
    }
    else
    {
      ++it;
    }
  }
IPCCommandResult CWII_IPC_HLE_Device_usb_oh0_46d_a03::IOCtlV(u32 CommandAddress)
{
	IPCCommandResult SendReply = GetNoReply();

	SIOCtlVBuffer CommandBuffer(CommandAddress);

	switch (CommandBuffer.Parameter)
	{
	case USBV0_IOCTL_CTRLMSG:
		{
			USBSetupPacket setup_packet;
			setup_packet.bmRequestType	= *( u8*)Memory::GetPointer(CommandBuffer.InBuffer[0].m_Address);
			setup_packet.bRequest		= *( u8*)Memory::GetPointer(CommandBuffer.InBuffer[1].m_Address);
			setup_packet.wValue			= *(u16*)Memory::GetPointer(CommandBuffer.InBuffer[2].m_Address);
			setup_packet.wIndex			= *(u16*)Memory::GetPointer(CommandBuffer.InBuffer[3].m_Address);
			setup_packet.wLength		= *(u16*)Memory::GetPointer(CommandBuffer.InBuffer[4].m_Address);

			const u32 payload_addr = CommandBuffer.PayloadBuffer[0].m_Address;

#define DIR_TO_DEV	0
#define DIR_TO_HOST	1
#define TYPE_STANDARD	0
#define TYPE_CLASS		1
#define TYPE_VENDOR		2
#define RECP_DEV	0
#define RECP_INT	1
#define RECP_ENDP	2
#define USBHDR(dir, type, recipient, request) \
	((((dir << 7) | (type << 5) | recipient) << 8) | request)

			switch (((u16)setup_packet.bmRequestType << 8) | setup_packet.bRequest)
			{
			case USBHDR(DIR_TO_HOST, TYPE_STANDARD, RECP_DEV, 6):
				// GET_DESCRIPTOR
				switch (setup_packet.wValue >> 8)
				{
					// CONFIGURATION
				case 2:
					{
					const usb_configurationdesc config = { 9, 2, 121, 2, 1, 3, 0x80, 30 };
					if (setup_packet.wLength == 9)
					{
						memcpy(Memory::GetPointer(payload_addr), &config, setup_packet.wLength);
					}
					else
					{
						#define LE24(x) (x & 0xff), ((x >> 8) & 0xff), (x >> 16)
						#pragma pack(push, 1)
						struct {
							usb_configurationdesc config;
							usb_interfacedesc int0;
							struct audiocontrol_hdr {
								u8 bLength;
								u8 bDescriptorType;
								u8 bDescriptorSubtype;
							};
							struct {
								audiocontrol_hdr hdr;
								u16 bcdADC;
								u16 wTotalLength;
								u8 bInCollection;
								u8 baInterfaceNr;
							} audiocontrol_header;
							struct {
								audiocontrol_hdr hdr;
								u8 bTerminalID;
								u16 wTerminalType;
								u8 bAssocTerminal;
								u8 bNrChannels;
								u16 wChannelConfig;
								u8 iChannelNames;
								u8 iTerminal;
							} audiocontrol_input_terminal;
							struct {
								audiocontrol_hdr hdr;
								u8 bUnitID;
								u8 bSourceID;
								u8 bControlSize;
								u8 bmaControls0;
								u8 bmaControls1;
								u8 iFeature;
							} audiocontrol_feature_unit;
							struct {
								audiocontrol_hdr hdr;
								u8 bTerminalID;
								u16 wTerminalType;
								u8 bAssocTerminal;
								u8 bSourceID;
								u8 iTerminal;
							} audiocontrol_output_terminal;
							usb_interfacedesc int1;
							usb_interfacedesc int2;
							struct {
								audiocontrol_hdr hdr;
								u8 bTerminalLink;
								u8 bDelay;
								u16 wFormatTag;
							} audiocontrol_as_general;
							struct {
								audiocontrol_hdr hdr;
								u8 bFormatType;
								u8 bNrChannels;
								u8 bSubframeSize;
								u8 bBitResolution;
								u8 bSamFreqType;
								u8 tSamFreq[3 * 5];
							} audiocontrol_format_type;
							usb_endpointdesc endp;
							struct {
								audiocontrol_hdr hdr;
								u8 bmAttributes;
								u8 bLockDelayUnits;
								u16 wLockDelay;
							} audiocontrol_ep_general;
						} const fullconfig = {
							config,
								{ 9, 4, 0, 0, 0, 1, 1, 0, 0 },
									{ 9, 36, 1, 0x100, 39, 1, 1 },
									{ 12, 36, 2, 13, 0x201, 0, 1, 0, 0, 0 },
									{ 9, 36, 6, 2, 13, 1, 3, 0 },
									{ 9, 36, 3, 10, 0x101, 0, 2, 0 },
								{ 9, 4, 1, 0, 0, 1, 2, 0, 0 },
								{ 9, 4, 1, 1, 1, 1, 2, 0, 0 },
									{ 7, 36, 1, 10, 0, 1 },
									{ 23, 36, 2, 1, 1, 2, 16, 5,
										LE24(8000), LE24(11025), LE24(22050),
										LE24(44100), LE24(48000) },
									{ 9, 5, 0x84, 13, 0x60, 1, 0, 0	},
										{ 7, 37, 1, 1, 2, 1 }
						};
						#pragma pack(pop)
						#undef LE24
						memcpy(Memory::GetPointer(payload_addr), &fullconfig, setup_packet.wLength);
					}

					Memory::Write_U32(sizeof(USBSetupPacket) + setup_packet.wLength, CommandAddress + 4);
					return GetDefaultReply();
					}
					break;

				default:
					goto outerdefault;
				}
				break;

			case USBHDR(DIR_TO_HOST, TYPE_CLASS, RECP_INT, 0x82):
			case USBHDR(DIR_TO_HOST, TYPE_CLASS, RECP_INT, 0x83):
				if (setup_packet.bRequest & 1)
					Memory::Write_U16(0x7fff, payload_addr);
				else
					Memory::Write_U16(0x8000, payload_addr);
				break;

			case USBHDR(DIR_TO_DEV, TYPE_CLASS, RECP_ENDP, 1):
				{
				u32 freq = *(u32*)Memory::GetPointer(payload_addr) & 0xffffff;
				WARN_LOG(OSHLE, "set freq: %x", freq);
				}
				break;

			case USBHDR(DIR_TO_DEV, TYPE_STANDARD, RECP_INT, 11):
				break;

			outerdefault:
			default:
				WARN_LOG(OSHLE, "UNK %02x %02x %04x %04x",
					setup_packet.bmRequestType, setup_packet.bRequest,
					setup_packet.wValue, setup_packet.wLength);
				break;
			}

			// command finished, send a reply to command
			WII_IPC_HLE_Interface::EnqueueReply(CommandBuffer.m_Address);
		}
		break;

	case USBV0_IOCTL_ISOMSG:
		{
		// endp 81 = mic -> console
		// endp 03 = console -> mic
		u8 endpoint = Memory::Read_U8(CommandBuffer.InBuffer[0].m_Address);
		u16 length = Memory::Read_U16(CommandBuffer.InBuffer[1].m_Address);
		u8 num_packets = Memory::Read_U8(CommandBuffer.InBuffer[2].m_Address);
		u16 *packet_sizes = (u16*)Memory::GetPointer(CommandBuffer.PayloadBuffer[0].m_Address);
		u8 *packets = Memory::GetPointer(CommandBuffer.PayloadBuffer[1].m_Address);

		u16 packet_len = Common::swap16(packet_sizes[0]);
		WARN_LOG(OSHLE, "%i to endpoint %02x", packet_len, endpoint);

		/*
		for (int i = 0; i < num_packets; i++)
		{
			u16 packet_len = Common::swap16(packet_sizes[i]);
			WARN_LOG(OSHLE, "packet %i [%i] to endpoint %02x", i, packet_len, endpoint);
			WARN_LOG(OSHLE, "%s", ArrayToString(packets, packet_len, 16).c_str());
			packets += packet_len;
		}
		*/
		
		if (endpoint == AUDIO_IN)
			for (u16 *sample = (u16*)packets; sample != (u16*)(packets + length); sample++)
				*sample = 0;
		
		// TODO actual responses should obey some kinda timey thing
		SendReply = GetDefaultReply();
		}
		break;

	default:
		WARN_LOG(OSHLE, "%s - IOCtlV:", GetDeviceName().c_str());
		WARN_LOG(OSHLE, "    Parameter: 0x%x", CommandBuffer.Parameter);
		WARN_LOG(OSHLE, "    NumberIn: 0x%08x", CommandBuffer.NumberInBuffer);
		WARN_LOG(OSHLE, "    NumberOut: 0x%08x", CommandBuffer.NumberPayloadBuffer);
		WARN_LOG(OSHLE, "    BufferVector: 0x%08x", CommandBuffer.BufferVector);
		//DumpAsync(CommandBuffer.BufferVector, CommandBuffer.NumberInBuffer, CommandBuffer.NumberPayloadBuffer, LogTypes::OSHLE, LogTypes::LWARNING);
		break;
	}

	Memory::Write_U32(0, CommandAddress + 4);
	return SendReply;
}
IPCCommandResult CWII_IPC_HLE_Device_usb_oh0_57e_308::IOCtlV(u32 CommandAddress)
{
	IPCCommandResult SendReply = GetNoReply();

	SIOCtlVBuffer CommandBuffer(CommandAddress);

	switch (CommandBuffer.Parameter)
	{
	case USBV0_IOCTL_CTRLMSG:
		{
			USBSetupPacket setup_packet;
			setup_packet.bmRequestType	= *( u8*)Memory::GetPointer(CommandBuffer.InBuffer[0].m_Address);
			setup_packet.bRequest		= *( u8*)Memory::GetPointer(CommandBuffer.InBuffer[1].m_Address);
			setup_packet.wValue			= *(u16*)Memory::GetPointer(CommandBuffer.InBuffer[2].m_Address);
			setup_packet.wIndex			= *(u16*)Memory::GetPointer(CommandBuffer.InBuffer[3].m_Address);
			setup_packet.wLength		= *(u16*)Memory::GetPointer(CommandBuffer.InBuffer[4].m_Address);

			const u32 payload_addr = CommandBuffer.PayloadBuffer[0].m_Address;

			static bool initialized = false;

#define DIR_TO_DEV	0
#define DIR_TO_HOST	1
#define TYPE_STANDARD	0
#define TYPE_VENDOR		2
#define RECP_DEV	0
#define RECP_INT	1
#define RECP_ENDP	2
#define USBHDR(dir, type, recipient, request) \
	((((dir << 7) | (type << 5) | recipient) << 8) | request)

			switch (((u16)setup_packet.bmRequestType << 8) | setup_packet.bRequest)
			{
			case USBHDR(DIR_TO_DEV, TYPE_STANDARD, RECP_INT, 11):
				_dbg_assert_(OSHLE, setup_packet.wValue == 1);
				break;
			case USBHDR(DIR_TO_HOST, TYPE_STANDARD, RECP_INT, 10):
				Memory::Write_U8(1, payload_addr);
				break;
			case USBHDR(DIR_TO_HOST, TYPE_VENDOR, RECP_INT, 6):
				if (!initialized)
					Memory::Write_U8(0, payload_addr), initialized = true;
				else
					Memory::Write_U8(1, payload_addr);
				break;
			case USBHDR(DIR_TO_DEV, TYPE_VENDOR, RECP_INT, 1):
				SetRegister(payload_addr);
				break;
			case USBHDR(DIR_TO_HOST, TYPE_VENDOR, RECP_INT, 2):
				GetRegister(payload_addr);
				break;
			case USBHDR(DIR_TO_DEV, TYPE_VENDOR, RECP_INT, 0):
				initialized = false;
				break;
			default:
				WARN_LOG(OSHLE, "UNK %02x %02x %04x %04x",
					setup_packet.bmRequestType, setup_packet.bRequest,
					setup_packet.wValue, setup_packet.wLength);
				break;
			}

			// command finished, send a reply to command
			WII_IPC_HLE_Interface::EnqueueReply(CommandBuffer.m_Address);
		}
		break;

	case USBV0_IOCTL_BLKMSG:
		{
		u8 Command = Memory::Read_U8(CommandBuffer.InBuffer[0].m_Address);

		switch (Command)
		{
		// used for sending firmware
		case DATA_OUT:
			{
			u16 len = Memory::Read_U16(CommandBuffer.InBuffer[1].m_Address);
			WARN_LOG(OSHLE, "SEND DATA %x %x %x", len, CommandBuffer.PayloadBuffer[0].m_Address, CommandBuffer.PayloadBuffer[0].m_Size);
			SendReply = GetDefaultReply();
			}
			break;

		default:
			WARN_LOG(OSHLE, "UNK BLKMSG %i", Command);
			break;
		}
		}
		break;

	case USBV0_IOCTL_ISOMSG:
		{
		// endp 81 = mic -> console
		// endp 03 = console -> mic
		u8 endpoint = Memory::Read_U8(CommandBuffer.InBuffer[0].m_Address);
		u16 length = Memory::Read_U16(CommandBuffer.InBuffer[1].m_Address);
		u8 num_packets = Memory::Read_U8(CommandBuffer.InBuffer[2].m_Address);
		u16 *packet_sizes = (u16*)Memory::GetPointer(CommandBuffer.PayloadBuffer[0].m_Address);
		u8 *packets = Memory::GetPointer(CommandBuffer.PayloadBuffer[1].m_Address);

		/*
		for (int i = 0; i < num_packets; i++)
		{
			u16 packet_len = Common::swap16(packet_sizes[i]);
			WARN_LOG(OSHLE, "packet %i [%i] to endpoint %02x", i, packet_len, endpoint);
			WARN_LOG(OSHLE, "%s", ArrayToString(packets, packet_len, 16).c_str());
			packets += packet_len;
		}
		*/
		
		if (endpoint == AUDIO_IN)
			for (u16 *sample = (u16*)packets; sample != (u16*)(packets + length); sample++)
				*sample = 0x8000;
		
		// TODO actual responses should obey some kinda timey thing
		SendReply = GetDefaultReply();
		}
		break;

	default:
		WARN_LOG(OSHLE, "%s - IOCtlV:", GetDeviceName().c_str());
		WARN_LOG(OSHLE, "    Parameter: 0x%x", CommandBuffer.Parameter);
		WARN_LOG(OSHLE, "    NumberIn: 0x%08x", CommandBuffer.NumberInBuffer);
		WARN_LOG(OSHLE, "    NumberOut: 0x%08x", CommandBuffer.NumberPayloadBuffer);
		WARN_LOG(OSHLE, "    BufferVector: 0x%08x", CommandBuffer.BufferVector);
		//DumpAsync(CommandBuffer.BufferVector, CommandBuffer.NumberInBuffer, CommandBuffer.NumberPayloadBuffer, LogTypes::OSHLE, LogTypes::LWARNING);
		break;
	}

	Memory::Write_U32(0, CommandAddress + 4);
	return SendReply;
}