mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	Merge pull request #3252 from Subv/fs_user
HLE/FS: Converted some functions to use IPCHelpers
This commit is contained in:
		
						commit
						4fd28e715e
					
				
					 4 changed files with 103 additions and 93 deletions
				
			
		|  | @ -14,19 +14,19 @@ namespace FileSys { | |||
| 
 | ||||
| Path::Path(LowPathType type, u32 size, u32 pointer) : type(type) { | ||||
|     switch (type) { | ||||
|     case Binary: { | ||||
|     case LowPathType::Binary: { | ||||
|         binary.resize(size); | ||||
|         Memory::ReadBlock(pointer, binary.data(), binary.size()); | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     case Char: { | ||||
|     case LowPathType::Char: { | ||||
|         string.resize(size - 1); // Data is always null-terminated.
 | ||||
|         Memory::ReadBlock(pointer, &string[0], string.size()); | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     case Wchar: { | ||||
|     case LowPathType::Wchar: { | ||||
|         u16str.resize(size / 2 - 1); // Data is always null-terminated.
 | ||||
|         Memory::ReadBlock(pointer, &u16str[0], u16str.size() * sizeof(char16_t)); | ||||
|         break; | ||||
|  | @ -39,12 +39,12 @@ Path::Path(LowPathType type, u32 size, u32 pointer) : type(type) { | |||
| 
 | ||||
| std::string Path::DebugStr() const { | ||||
|     switch (GetType()) { | ||||
|     case Invalid: | ||||
|     case LowPathType::Invalid: | ||||
|     default: | ||||
|         return "[Invalid]"; | ||||
|     case Empty: | ||||
|     case LowPathType::Empty: | ||||
|         return "[Empty]"; | ||||
|     case Binary: { | ||||
|     case LowPathType::Binary: { | ||||
|         std::stringstream res; | ||||
|         res << "[Binary: "; | ||||
|         for (unsigned byte : binary) | ||||
|  | @ -52,23 +52,23 @@ std::string Path::DebugStr() const { | |||
|         res << ']'; | ||||
|         return res.str(); | ||||
|     } | ||||
|     case Char: | ||||
|     case LowPathType::Char: | ||||
|         return "[Char: " + AsString() + ']'; | ||||
|     case Wchar: | ||||
|     case LowPathType::Wchar: | ||||
|         return "[Wchar: " + AsString() + ']'; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::string Path::AsString() const { | ||||
|     switch (GetType()) { | ||||
|     case Char: | ||||
|     case LowPathType::Char: | ||||
|         return string; | ||||
|     case Wchar: | ||||
|     case LowPathType::Wchar: | ||||
|         return Common::UTF16ToUTF8(u16str); | ||||
|     case Empty: | ||||
|     case LowPathType::Empty: | ||||
|         return {}; | ||||
|     case Invalid: | ||||
|     case Binary: | ||||
|     case LowPathType::Invalid: | ||||
|     case LowPathType::Binary: | ||||
|     default: | ||||
|         // TODO(yuriks): Add assert
 | ||||
|         LOG_ERROR(Service_FS, "LowPathType cannot be converted to string!"); | ||||
|  | @ -78,14 +78,14 @@ std::string Path::AsString() const { | |||
| 
 | ||||
| std::u16string Path::AsU16Str() const { | ||||
|     switch (GetType()) { | ||||
|     case Char: | ||||
|     case LowPathType::Char: | ||||
|         return Common::UTF8ToUTF16(string); | ||||
|     case Wchar: | ||||
|     case LowPathType::Wchar: | ||||
|         return u16str; | ||||
|     case Empty: | ||||
|     case LowPathType::Empty: | ||||
|         return {}; | ||||
|     case Invalid: | ||||
|     case Binary: | ||||
|     case LowPathType::Invalid: | ||||
|     case LowPathType::Binary: | ||||
|         // TODO(yuriks): Add assert
 | ||||
|         LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!"); | ||||
|         return {}; | ||||
|  | @ -96,11 +96,11 @@ std::u16string Path::AsU16Str() const { | |||
| 
 | ||||
| std::vector<u8> Path::AsBinary() const { | ||||
|     switch (GetType()) { | ||||
|     case Binary: | ||||
|     case LowPathType::Binary: | ||||
|         return binary; | ||||
|     case Char: | ||||
|     case LowPathType::Char: | ||||
|         return std::vector<u8>(string.begin(), string.end()); | ||||
|     case Wchar: { | ||||
|     case LowPathType::Wchar: { | ||||
|         // use two u8 for each character of u16str
 | ||||
|         std::vector<u8> to_return(u16str.size() * 2); | ||||
|         for (size_t i = 0; i < u16str.size(); ++i) { | ||||
|  | @ -110,9 +110,9 @@ std::vector<u8> Path::AsBinary() const { | |||
|         } | ||||
|         return to_return; | ||||
|     } | ||||
|     case Empty: | ||||
|     case LowPathType::Empty: | ||||
|         return {}; | ||||
|     case Invalid: | ||||
|     case LowPathType::Invalid: | ||||
|     default: | ||||
|         // TODO(yuriks): Add assert
 | ||||
|         LOG_ERROR(Service_FS, "LowPathType cannot be converted to binary!"); | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ class FileBackend; | |||
| class DirectoryBackend; | ||||
| 
 | ||||
| // Path string type
 | ||||
| enum LowPathType : u32 { | ||||
| enum class LowPathType : u32 { | ||||
|     Invalid = 0, | ||||
|     Empty = 1, | ||||
|     Binary = 2, | ||||
|  | @ -36,9 +36,9 @@ union Mode { | |||
| 
 | ||||
| class Path { | ||||
| public: | ||||
|     Path() : type(Invalid) {} | ||||
|     Path(const char* path) : type(Char), string(path) {} | ||||
|     Path(std::vector<u8> binary_data) : type(Binary), binary(std::move(binary_data)) {} | ||||
|     Path() : type(LowPathType::Invalid) {} | ||||
|     Path(const char* path) : type(LowPathType::Char), string(path) {} | ||||
|     Path(std::vector<u8> binary_data) : type(LowPathType::Binary), binary(std::move(binary_data)) {} | ||||
|     Path(LowPathType type, u32 size, u32 pointer); | ||||
| 
 | ||||
|     LowPathType GetType() const { | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ namespace { | |||
| 
 | ||||
| template <typename T> | ||||
| ResultVal<std::tuple<MediaType, u64>> ParsePath(const Path& path, T program_id_reader) { | ||||
|     if (path.GetType() != Binary) { | ||||
|     if (path.GetType() != LowPathType::Binary) { | ||||
|         LOG_ERROR(Service_FS, "Wrong path type %d", static_cast<int>(path.GetType())); | ||||
|         return ERROR_INVALID_PATH; | ||||
|     } | ||||
|  |  | |||
|  | @ -69,10 +69,9 @@ static void OpenFile(Service::Interface* self) { | |||
|     rp.Pop<u32>(); // Always 0 ?
 | ||||
| 
 | ||||
|     ArchiveHandle archive_handle = rp.Pop<u64>(); | ||||
|     auto filename_type = static_cast<FileSys::LowPathType>(rp.Pop<u32>()); | ||||
|     auto filename_type = rp.PopEnum<FileSys::LowPathType>(); | ||||
|     u32 filename_size = rp.Pop<u32>(); | ||||
|     FileSys::Mode mode; | ||||
|     mode.hex = rp.Pop<u32>(); | ||||
|     FileSys::Mode mode{rp.Pop<u32>()}; | ||||
|     u32 attributes = rp.Pop<u32>(); // TODO(Link Mauve): do something with those attributes.
 | ||||
|     VAddr filename_ptr = rp.PopStaticBuffer(nullptr); | ||||
|     FileSys::Path file_path(filename_type, filename_size, filename_ptr); | ||||
|  | @ -113,18 +112,18 @@ static void OpenFile(Service::Interface* self) { | |||
|  *      3 : File handle | ||||
|  */ | ||||
| static void OpenFileDirectly(Service::Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x803, 8, 4); | ||||
|     rp.Skip(1, false); // Transaction
 | ||||
| 
 | ||||
|     auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[2]); | ||||
|     auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[3]); | ||||
|     u32 archivename_size = cmd_buff[4]; | ||||
|     auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[5]); | ||||
|     u32 filename_size = cmd_buff[6]; | ||||
|     FileSys::Mode mode; | ||||
|     mode.hex = cmd_buff[7]; | ||||
|     u32 attributes = cmd_buff[8]; // TODO(Link Mauve): do something with those attributes.
 | ||||
|     u32 archivename_ptr = cmd_buff[10]; | ||||
|     u32 filename_ptr = cmd_buff[12]; | ||||
|     auto archive_id = rp.PopEnum<FS::ArchiveIdCode>(); | ||||
|     auto archivename_type = rp.PopEnum<FileSys::LowPathType>(); | ||||
|     u32 archivename_size = rp.Pop<u32>(); | ||||
|     auto filename_type = rp.PopEnum<FileSys::LowPathType>(); | ||||
|     u32 filename_size = rp.Pop<u32>(); | ||||
|     FileSys::Mode mode{rp.Pop<u32>()}; | ||||
|     u32 attributes = rp.Pop<u32>(); // TODO(Link Mauve): do something with those attributes.
 | ||||
|     u32 archivename_ptr = rp.PopStaticBuffer(nullptr); | ||||
|     u32 filename_ptr = rp.PopStaticBuffer(nullptr); | ||||
|     FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr); | ||||
|     FileSys::Path file_path(filename_type, filename_size, filename_ptr); | ||||
| 
 | ||||
|  | @ -132,25 +131,27 @@ static void OpenFileDirectly(Service::Interface* self) { | |||
|               static_cast<u32>(archive_id), archive_path.DebugStr().c_str(), | ||||
|               file_path.DebugStr().c_str(), mode.hex, attributes); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | ||||
| 
 | ||||
|     ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id, archive_path); | ||||
|     if (archive_handle.Failed()) { | ||||
|         LOG_ERROR(Service_FS, | ||||
|                   "Failed to get a handle for archive archive_id=0x%08X archive_path=%s", | ||||
|                   static_cast<u32>(archive_id), archive_path.DebugStr().c_str()); | ||||
|         cmd_buff[1] = archive_handle.Code().raw; | ||||
|         cmd_buff[3] = 0; | ||||
|         rb.Push(archive_handle.Code()); | ||||
|         rb.PushMoveHandles(0); | ||||
|         return; | ||||
|     } | ||||
|     SCOPE_EXIT({ CloseArchive(*archive_handle); }); | ||||
| 
 | ||||
|     ResultVal<std::shared_ptr<File>> file_res = | ||||
|         OpenFileFromArchive(*archive_handle, file_path, mode); | ||||
|     cmd_buff[1] = file_res.Code().raw; | ||||
|     rb.Push(file_res.Code()); | ||||
|     if (file_res.Succeeded()) { | ||||
|         std::shared_ptr<File> file = *file_res; | ||||
|         cmd_buff[3] = Kernel::g_handle_table.Create(file->Connect()).Unwrap(); | ||||
|         rb.PushMoveHandles(Kernel::g_handle_table.Create(file->Connect()).Unwrap()); | ||||
|     } else { | ||||
|         cmd_buff[3] = 0; | ||||
|         rb.PushMoveHandles(0); | ||||
|         LOG_ERROR(Service_FS, "failed to get a handle for file %s mode=%u attributes=%u", | ||||
|                   file_path.DebugStr().c_str(), mode.hex, attributes); | ||||
|     } | ||||
|  | @ -168,19 +169,20 @@ static void OpenFileDirectly(Service::Interface* self) { | |||
|  *      1 : Result of function, 0 on success, otherwise error code | ||||
|  */ | ||||
| static void DeleteFile(Service::Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
| 
 | ||||
|     ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); | ||||
|     auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||||
|     u32 filename_size = cmd_buff[5]; | ||||
|     u32 filename_ptr = cmd_buff[7]; | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x804, 5, 2); | ||||
|     rp.Skip(1, false); // TransactionId
 | ||||
|     ArchiveHandle archive_handle = rp.PopRaw<ArchiveHandle>(); | ||||
|     auto filename_type = rp.PopEnum<FileSys::LowPathType>(); | ||||
|     u32 filename_size = rp.Pop<u32>(); | ||||
|     u32 filename_ptr = rp.PopStaticBuffer(nullptr); | ||||
| 
 | ||||
|     FileSys::Path file_path(filename_type, filename_size, filename_ptr); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_FS, "type=%u size=%u data=%s", static_cast<u32>(filename_type), filename_size, | ||||
|               file_path.DebugStr().c_str()); | ||||
| 
 | ||||
|     cmd_buff[1] = DeleteFileFromArchive(archive_handle, file_path).raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(DeleteFileFromArchive(archive_handle, file_path)); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -200,17 +202,17 @@ static void DeleteFile(Service::Interface* self) { | |||
|  *      1 : Result of function, 0 on success, otherwise error code | ||||
|  */ | ||||
| static void RenameFile(Service::Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x805, 9, 4); | ||||
|     rp.Skip(1, false); // TransactionId
 | ||||
| 
 | ||||
|     ArchiveHandle src_archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); | ||||
|     auto src_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||||
|     u32 src_filename_size = cmd_buff[5]; | ||||
|     ArchiveHandle dest_archive_handle = MakeArchiveHandle(cmd_buff[6], cmd_buff[7]); | ||||
|     ; | ||||
|     auto dest_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[8]); | ||||
|     u32 dest_filename_size = cmd_buff[9]; | ||||
|     u32 src_filename_ptr = cmd_buff[11]; | ||||
|     u32 dest_filename_ptr = cmd_buff[13]; | ||||
|     ArchiveHandle src_archive_handle = rp.PopRaw<ArchiveHandle>(); | ||||
|     auto src_filename_type = rp.PopEnum<FileSys::LowPathType>(); | ||||
|     u32 src_filename_size = rp.Pop<u32>(); | ||||
|     ArchiveHandle dest_archive_handle = rp.PopRaw<ArchiveHandle>(); | ||||
|     auto dest_filename_type = rp.PopEnum<FileSys::LowPathType>(); | ||||
|     u32 dest_filename_size = rp.Pop<u32>(); | ||||
|     u32 src_filename_ptr = rp.PopStaticBuffer(nullptr); | ||||
|     u32 dest_filename_ptr = rp.PopStaticBuffer(nullptr); | ||||
| 
 | ||||
|     FileSys::Path src_file_path(src_filename_type, src_filename_size, src_filename_ptr); | ||||
|     FileSys::Path dest_file_path(dest_filename_type, dest_filename_size, dest_filename_ptr); | ||||
|  | @ -221,9 +223,9 @@ static void RenameFile(Service::Interface* self) { | |||
|               src_file_path.DebugStr().c_str(), static_cast<u32>(dest_filename_type), | ||||
|               dest_filename_size, dest_file_path.DebugStr().c_str()); | ||||
| 
 | ||||
|     cmd_buff[1] = RenameFileBetweenArchives(src_archive_handle, src_file_path, dest_archive_handle, | ||||
|                                             dest_file_path) | ||||
|                       .raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RenameFileBetweenArchives(src_archive_handle, src_file_path, dest_archive_handle, | ||||
|                                       dest_file_path)); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -238,19 +240,21 @@ static void RenameFile(Service::Interface* self) { | |||
|  *      1 : Result of function, 0 on success, otherwise error code | ||||
|  */ | ||||
| static void DeleteDirectory(Service::Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x806, 5, 2); | ||||
| 
 | ||||
|     ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); | ||||
|     auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||||
|     u32 dirname_size = cmd_buff[5]; | ||||
|     u32 dirname_ptr = cmd_buff[7]; | ||||
|     rp.Skip(1, false); // TransactionId
 | ||||
|     ArchiveHandle archive_handle = rp.PopRaw<ArchiveHandle>(); | ||||
|     auto dirname_type = rp.PopEnum<FileSys::LowPathType>(); | ||||
|     u32 dirname_size = rp.Pop<u32>(); | ||||
|     u32 dirname_ptr = rp.PopStaticBuffer(nullptr); | ||||
| 
 | ||||
|     FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_FS, "type=%u size=%u data=%s", static_cast<u32>(dirname_type), dirname_size, | ||||
|               dir_path.DebugStr().c_str()); | ||||
| 
 | ||||
|     cmd_buff[1] = DeleteDirectoryFromArchive(archive_handle, dir_path).raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(DeleteDirectoryFromArchive(archive_handle, dir_path)); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -267,19 +271,21 @@ static void DeleteDirectory(Service::Interface* self) { | |||
|  *      1 : Result of function, 0 on success, otherwise error code | ||||
|  */ | ||||
| static void DeleteDirectoryRecursively(Service::Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x807, 5, 2); | ||||
| 
 | ||||
|     ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); | ||||
|     auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||||
|     u32 dirname_size = cmd_buff[5]; | ||||
|     u32 dirname_ptr = cmd_buff[7]; | ||||
|     rp.Skip(1, false); // TransactionId
 | ||||
|     ArchiveHandle archive_handle = rp.PopRaw<ArchiveHandle>(); | ||||
|     auto dirname_type = rp.PopEnum<FileSys::LowPathType>(); | ||||
|     u32 dirname_size = rp.Pop<u32>(); | ||||
|     u32 dirname_ptr = rp.PopStaticBuffer(nullptr); | ||||
| 
 | ||||
|     FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_FS, "type=%u size=%u data=%s", static_cast<u32>(dirname_type), dirname_size, | ||||
|               dir_path.DebugStr().c_str()); | ||||
| 
 | ||||
|     cmd_buff[1] = DeleteDirectoryRecursivelyFromArchive(archive_handle, dir_path).raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(DeleteDirectoryRecursivelyFromArchive(archive_handle, dir_path)); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -290,26 +296,30 @@ static void DeleteDirectoryRecursively(Service::Interface* self) { | |||
|  *      3 : Archive handle upper word | ||||
|  *      4 : File path string type | ||||
|  *      5 : File path string size | ||||
|  *      6 : File attributes | ||||
|  *      7-8 : File size | ||||
|  *      10: File path string data | ||||
|  *  Outputs: | ||||
|  *      1 : Result of function, 0 on success, otherwise error code | ||||
|  */ | ||||
| static void CreateFile(Service::Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x808, 8, 2); | ||||
| 
 | ||||
|     ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); | ||||
|     auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||||
|     u32 filename_size = cmd_buff[5]; | ||||
|     u64 file_size = ((u64)cmd_buff[8] << 32) | cmd_buff[7]; | ||||
|     u32 filename_ptr = cmd_buff[10]; | ||||
|     rp.Skip(1, false); // TransactionId
 | ||||
|     ArchiveHandle archive_handle = rp.PopRaw<ArchiveHandle>(); | ||||
|     auto filename_type = rp.PopEnum<FileSys::LowPathType>(); | ||||
|     u32 filename_size = rp.Pop<u32>(); | ||||
|     u32 attributes = rp.Pop<u32>(); | ||||
|     u64 file_size = rp.Pop<u64>(); | ||||
|     u32 filename_ptr = rp.PopStaticBuffer(nullptr); | ||||
| 
 | ||||
|     FileSys::Path file_path(filename_type, filename_size, filename_ptr); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_FS, "type=%u size=%" PRIx64 " data=%s", static_cast<u32>(filename_type), | ||||
|               file_size, file_path.DebugStr().c_str()); | ||||
|     LOG_DEBUG(Service_FS, "type=%u attributes=%u size=%" PRIx64 " data=%s", | ||||
|               static_cast<u32>(filename_type), attributes, file_size, file_path.DebugStr().c_str()); | ||||
| 
 | ||||
|     cmd_buff[1] = CreateFileInArchive(archive_handle, file_path, file_size).raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(CreateFileInArchive(archive_handle, file_path, file_size)); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -478,11 +488,11 @@ static void CloseArchive(Service::Interface* self) { | |||
| } | ||||
| 
 | ||||
| /*
 | ||||
| * FS_User::IsSdmcDetected service function | ||||
| *  Outputs: | ||||
| *      1 : Result of function, 0 on success, otherwise error code | ||||
| *      2 : Whether the Sdmc could be detected | ||||
| */ | ||||
|  * FS_User::IsSdmcDetected service function | ||||
|  *  Outputs: | ||||
|  *      1 : Result of function, 0 on success, otherwise error code | ||||
|  *      2 : Whether the Sdmc could be detected | ||||
|  */ | ||||
| static void IsSdmcDetected(Service::Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue