diff --git a/troposphere/haze/include/haze/assert.hpp b/troposphere/haze/include/haze/assert.hpp index 9f3ac1201..d7178276b 100644 --- a/troposphere/haze/include/haze/assert.hpp +++ b/troposphere/haze/include/haze/assert.hpp @@ -28,3 +28,5 @@ const auto _tmp_r_abort_rc = (res_expr); \ HAZE_ASSERT(R_SUCCEEDED(_tmp_r_abort_rc)); \ } + +#define HAZE_UNREACHABLE_DEFAULT_CASE() default: HAZE_ASSERT(false) diff --git a/troposphere/haze/include/haze/ptp.hpp b/troposphere/haze/include/haze/ptp.hpp index d9b6e74e0..216767ef6 100644 --- a/troposphere/haze/include/haze/ptp.hpp +++ b/troposphere/haze/include/haze/ptp.hpp @@ -123,15 +123,15 @@ namespace haze { PtpResponseCode_NoStreamEnabled = 0x2022, PtpResponseCode_InvalidDataSet = 0x2023, PtpResponseCode_MtpUndefined = 0xa800, - PtpResponseCode_MtpInvalid_ObjectPropCode = 0xa801, - PtpResponseCode_MtpInvalid_ObjectProp_Format = 0xa802, - PtpResponseCode_MtpInvalid_ObjectProp_Value = 0xa803, - PtpResponseCode_MtpInvalid_ObjectReference = 0xa804, - PtpResponseCode_MtpInvalid_Dataset = 0xa806, - PtpResponseCode_MtpSpecification_By_Group_Unsupported = 0xa807, - PtpResponseCode_MtpSpecification_By_Depth_Unsupported = 0xa808, - PtpResponseCode_MtpObject_Too_Large = 0xa809, - PtpResponseCode_MtpObjectProp_Not_Supported = 0xa80a, + PtpResponseCode_MtpInvalidObjectPropCode = 0xa801, + PtpResponseCode_MtpInvalidObjectPropFormat = 0xa802, + PtpResponseCode_MtpInvalidObjectPropValue = 0xa803, + PtpResponseCode_MtpInvalidObjectReference = 0xa804, + PtpResponseCode_MtpInvalidDataset = 0xa806, + PtpResponseCode_MtpSpecificationByGroupUnsupported = 0xa807, + PtpResponseCode_MtpSpecificationByDepthUnsupported = 0xa808, + PtpResponseCode_MtpObjectTooLarge = 0xa809, + PtpResponseCode_MtpObjectPropNotSupported = 0xa80a, }; enum PtpEventCode : u16 { @@ -152,6 +152,225 @@ namespace haze { PtpEventCode_UnreportedStatus = 0x400e, }; + enum PtpDataTypeCode : u16 { + PtpDataTypeCode_Undefined = 0x0000, + PtpDataTypeCode_S8 = 0x0001, + PtpDataTypeCode_U8 = 0x0002, + PtpDataTypeCode_S16 = 0x0003, + PtpDataTypeCode_U16 = 0x0004, + PtpDataTypeCode_S32 = 0x0005, + PtpDataTypeCode_U32 = 0x0006, + PtpDataTypeCode_S64 = 0x0007, + PtpDataTypeCode_U64 = 0x0008, + PtpDataTypeCode_S128 = 0x0009, + PtpDataTypeCode_U128 = 0x000a, + + PtpDataTypeCode_ArrayMask = (1u << 14), + + PtpDataTypeCode_S8Array = PtpDataTypeCode_ArrayMask | PtpDataTypeCode_S8, + PtpDataTypeCode_U8Array = PtpDataTypeCode_ArrayMask | PtpDataTypeCode_U8, + PtpDataTypeCode_S16Array = PtpDataTypeCode_ArrayMask | PtpDataTypeCode_S16, + PtpDataTypeCode_U16Array = PtpDataTypeCode_ArrayMask | PtpDataTypeCode_U16, + PtpDataTypeCode_S32Array = PtpDataTypeCode_ArrayMask | PtpDataTypeCode_S32, + PtpDataTypeCode_U32Array = PtpDataTypeCode_ArrayMask | PtpDataTypeCode_U32, + PtpDataTypeCode_S64Array = PtpDataTypeCode_ArrayMask | PtpDataTypeCode_S64, + PtpDataTypeCode_U64Array = PtpDataTypeCode_ArrayMask | PtpDataTypeCode_U64, + PtpDataTypeCode_S128Array = PtpDataTypeCode_ArrayMask | PtpDataTypeCode_S128, + PtpDataTypeCode_U128Array = PtpDataTypeCode_ArrayMask | PtpDataTypeCode_U128, + + PtpDataTypeCode_String = 0xffff, + }; + + enum PtpPropertyGetSetFlag : u8 { + PtpPropertyGetSetFlag_Get = 0x00, + PtpPropertyGetSetFlag_GetSet = 0x01, + }; + + enum PtpPropertyGroupCode : u32 { + PtpPropertyGroupCode_Default = 0x00000000, + }; + + enum PtpPropertyFormFlag : u8 { + PtpPropertyFormFlag_None = 0x00, + PtpPropertyFormFlag_Range = 0x01, + PtpPropertyFormFlag_Enumeration = 0x02, + PtpPropertyFormFlag_DateTime = 0x03, + PtpPropertyFormFlag_FixedLengthArray = 0x04, + PtpPropertyFormFlag_RegularExpression = 0x05, + PtpPropertyFormFlag_ByteArray = 0x06, + PtpPropertyFormFlag_LongString = 0xff, + }; + + enum PtpObjectPropertyCode : u16 { + PtpObjectPropertyCode_StorageId = 0xdc01, + PtpObjectPropertyCode_ObjectFormat = 0xdc02, + PtpObjectPropertyCode_ProtectionStatus = 0xdc03, + PtpObjectPropertyCode_ObjectSize = 0xdc04, + PtpObjectPropertyCode_AssociationType = 0xdc05, + PtpObjectPropertyCode_AssociationDesc = 0xdc06, + PtpObjectPropertyCode_ObjectFileName = 0xdc07, + PtpObjectPropertyCode_DateCreated = 0xdc08, + PtpObjectPropertyCode_DateModified = 0xdc09, + PtpObjectPropertyCode_Keywords = 0xdc0a, + PtpObjectPropertyCode_ParentObject = 0xdc0b, + PtpObjectPropertyCode_AllowedFolderContents = 0xdc0c, + PtpObjectPropertyCode_Hidden = 0xdc0d, + PtpObjectPropertyCode_SystemObject = 0xdc0e, + PtpObjectPropertyCode_PersistantUniqueObjectIdentifier = 0xdc41, + PtpObjectPropertyCode_SyncId = 0xdc42, + PtpObjectPropertyCode_PropertyBag = 0xdc43, + PtpObjectPropertyCode_Name = 0xdc44, + PtpObjectPropertyCode_CreatedBy = 0xdc45, + PtpObjectPropertyCode_Artist = 0xdc46, + PtpObjectPropertyCode_DateAuthored = 0xdc47, + PtpObjectPropertyCode_Description = 0xdc48, + PtpObjectPropertyCode_UrlReference = 0xdc49, + PtpObjectPropertyCode_LanguageLocale = 0xdc4a, + PtpObjectPropertyCode_CopyrightInformation = 0xdc4b, + PtpObjectPropertyCode_Source = 0xdc4c, + PtpObjectPropertyCode_OriginLocation = 0xdc4d, + PtpObjectPropertyCode_DateAdded = 0xdc4e, + PtpObjectPropertyCode_NonConsumable = 0xdc4f, + PtpObjectPropertyCode_CorruptOrUnplayable = 0xdc50, + PtpObjectPropertyCode_ProducerSerialNumber = 0xdc51, + PtpObjectPropertyCode_RepresentativeSampleFormat = 0xdc81, + PtpObjectPropertyCode_RepresentativeSampleSize = 0xdc82, + PtpObjectPropertyCode_RepresentativeSampleHeight = 0xdc83, + PtpObjectPropertyCode_RepresentativeSampleWidth = 0xdc84, + PtpObjectPropertyCode_RepresentativeSampleDuration = 0xdc85, + PtpObjectPropertyCode_RepresentativeSampleData = 0xdc86, + PtpObjectPropertyCode_Width = 0xdc87, + PtpObjectPropertyCode_Height = 0xdc88, + PtpObjectPropertyCode_Duration = 0xdc89, + PtpObjectPropertyCode_Rating = 0xdc8a, + PtpObjectPropertyCode_Track = 0xdc8b, + PtpObjectPropertyCode_Genre = 0xdc8c, + PtpObjectPropertyCode_Credits = 0xdc8d, + PtpObjectPropertyCode_Lyrics = 0xdc8e, + PtpObjectPropertyCode_SubscriptionContentId = 0xdc8f, + PtpObjectPropertyCode_ProducedBy = 0xdc90, + PtpObjectPropertyCode_UseCount = 0xdc91, + PtpObjectPropertyCode_SkipCount = 0xdc92, + PtpObjectPropertyCode_LastAccessed = 0xdc93, + PtpObjectPropertyCode_ParentalRating = 0xdc94, + PtpObjectPropertyCode_MetaGenre = 0xdc95, + PtpObjectPropertyCode_Composer = 0xdc96, + PtpObjectPropertyCode_EffectiveRating = 0xdc97, + PtpObjectPropertyCode_Subtitle = 0xdc98, + PtpObjectPropertyCode_OriginalReleaseDate = 0xdc99, + PtpObjectPropertyCode_AlbumName = 0xdc9a, + PtpObjectPropertyCode_AlbumArtist = 0xdc9b, + PtpObjectPropertyCode_Mood = 0xdc9c, + PtpObjectPropertyCode_DrmStatus = 0xdc9d, + PtpObjectPropertyCode_SubDescription = 0xdc9e, + PtpObjectPropertyCode_IsCropped = 0xdcd1, + PtpObjectPropertyCode_IsColorCorrected = 0xdcd2, + PtpObjectPropertyCode_ImageBitDepth = 0xdcd3, + PtpObjectPropertyCode_Fnumber = 0xdcd4, + PtpObjectPropertyCode_ExposureTime = 0xdcd5, + PtpObjectPropertyCode_ExposureIndex = 0xdcd6, + PtpObjectPropertyCode_DisplayName = 0xdce0, + PtpObjectPropertyCode_BodyText = 0xdce1, + PtpObjectPropertyCode_Subject = 0xdce2, + PtpObjectPropertyCode_Priority = 0xdce3, + PtpObjectPropertyCode_GivenName = 0xdd00, + PtpObjectPropertyCode_MiddleNames = 0xdd01, + PtpObjectPropertyCode_FamilyName = 0xdd02, + PtpObjectPropertyCode_Prefix = 0xdd03, + PtpObjectPropertyCode_Suffix = 0xdd04, + PtpObjectPropertyCode_PhoneticGivenName = 0xdd05, + PtpObjectPropertyCode_PhoneticFamilyName = 0xdd06, + PtpObjectPropertyCode_EmailPrimary = 0xdd07, + PtpObjectPropertyCode_EmailPersonal1 = 0xdd08, + PtpObjectPropertyCode_EmailPersonal2 = 0xdd09, + PtpObjectPropertyCode_EmailBusiness1 = 0xdd0a, + PtpObjectPropertyCode_EmailBusiness2 = 0xdd0b, + PtpObjectPropertyCode_EmailOthers = 0xdd0c, + PtpObjectPropertyCode_PhoneNumberPrimary = 0xdd0d, + PtpObjectPropertyCode_PhoneNumberPersonal = 0xdd0e, + PtpObjectPropertyCode_PhoneNumberPersonal2 = 0xdd0f, + PtpObjectPropertyCode_PhoneNumberBusiness = 0xdd10, + PtpObjectPropertyCode_PhoneNumberBusiness2 = 0xdd11, + PtpObjectPropertyCode_PhoneNumberMobile = 0xdd12, + PtpObjectPropertyCode_PhoneNumberMobile2 = 0xdd13, + PtpObjectPropertyCode_FaxNumberPrimary = 0xdd14, + PtpObjectPropertyCode_FaxNumberPersonal = 0xdd15, + PtpObjectPropertyCode_FaxNumberBusiness = 0xdd16, + PtpObjectPropertyCode_PagerNumber = 0xdd17, + PtpObjectPropertyCode_PhoneNumberOthers = 0xdd18, + PtpObjectPropertyCode_PrimaryWebAddress = 0xdd19, + PtpObjectPropertyCode_PersonalWebAddress = 0xdd1a, + PtpObjectPropertyCode_BusinessWebAddress = 0xdd1b, + PtpObjectPropertyCode_InstantMessengerAddress = 0xdd1c, + PtpObjectPropertyCode_InstantMessengerAddress2 = 0xdd1d, + PtpObjectPropertyCode_InstantMessengerAddress3 = 0xdd1e, + PtpObjectPropertyCode_PostalAddressPersonalFull = 0xdd1f, + PtpObjectPropertyCode_PostalAddressPersonalFullLine1 = 0xdd20, + PtpObjectPropertyCode_PostalAddressPersonalFullLine2 = 0xdd21, + PtpObjectPropertyCode_PostalAddressPersonalFullCity = 0xdd22, + PtpObjectPropertyCode_PostalAddressPersonalFullRegion = 0xdd23, + PtpObjectPropertyCode_PostalAddressPersonalFullPostalCode = 0xdd24, + PtpObjectPropertyCode_PostalAddressPersonalFullCountry = 0xdd25, + PtpObjectPropertyCode_PostalAddressBusinessFull = 0xdd26, + PtpObjectPropertyCode_PostalAddressBusinessLine1 = 0xdd27, + PtpObjectPropertyCode_PostalAddressBusinessLine2 = 0xdd28, + PtpObjectPropertyCode_PostalAddressBusinessCity = 0xdd29, + PtpObjectPropertyCode_PostalAddressBusinessRegion = 0xdd2a, + PtpObjectPropertyCode_PostalAddressBusinessPostalCode = 0xdd2b, + PtpObjectPropertyCode_PostalAddressBusinessCountry = 0xdd2c, + PtpObjectPropertyCode_PostalAddressOtherFull = 0xdd2d, + PtpObjectPropertyCode_PostalAddressOtherLine1 = 0xdd2e, + PtpObjectPropertyCode_PostalAddressOtherLine2 = 0xdd2f, + PtpObjectPropertyCode_PostalAddressOtherCity = 0xdd30, + PtpObjectPropertyCode_PostalAddressOtherRegion = 0xdd31, + PtpObjectPropertyCode_PostalAddressOtherPostalCode = 0xdd32, + PtpObjectPropertyCode_PostalAddressOtherCountry = 0xdd33, + PtpObjectPropertyCode_OrganizationName = 0xdd34, + PtpObjectPropertyCode_PhoneticOrganizationName = 0xdd35, + PtpObjectPropertyCode_Role = 0xdd36, + PtpObjectPropertyCode_Birthdate = 0xdd37, + PtpObjectPropertyCode_MessageTo = 0xdd40, + PtpObjectPropertyCode_MessageCC = 0xdd41, + PtpObjectPropertyCode_MessageBCC = 0xdd42, + PtpObjectPropertyCode_MessageRead = 0xdd43, + PtpObjectPropertyCode_MessageReceivedTime = 0xdd44, + PtpObjectPropertyCode_MessageSender = 0xdd45, + PtpObjectPropertyCode_ActivityBeginTime = 0xdd50, + PtpObjectPropertyCode_ActivityEndTime = 0xdd51, + PtpObjectPropertyCode_ActivityLocation = 0xdd52, + PtpObjectPropertyCode_ActivityRequiredAttendees = 0xdd54, + PtpObjectPropertyCode_ActivityOptionalAttendees = 0xdd55, + PtpObjectPropertyCode_ActivityResources = 0xdd56, + PtpObjectPropertyCode_ActivityAccepted = 0xdd57, + PtpObjectPropertyCode_Owner = 0xdd5d, + PtpObjectPropertyCode_Editor = 0xdd5e, + PtpObjectPropertyCode_Webmaster = 0xdd5f, + PtpObjectPropertyCode_UrlSource = 0xdd60, + PtpObjectPropertyCode_UrlDestination = 0xdd61, + PtpObjectPropertyCode_TimeBookmark = 0xdd62, + PtpObjectPropertyCode_ObjectBookmark = 0xdd63, + PtpObjectPropertyCode_ByteBookmark = 0xdd64, + PtpObjectPropertyCode_LastBuildDate = 0xdd70, + PtpObjectPropertyCode_TimetoLive = 0xdd71, + PtpObjectPropertyCode_MediaGuid = 0xdd72, + PtpObjectPropertyCode_TotalBitRate = 0xde91, + PtpObjectPropertyCode_BitRateType = 0xde92, + PtpObjectPropertyCode_SampleRate = 0xde93, + PtpObjectPropertyCode_NumberOfChannels = 0xde94, + PtpObjectPropertyCode_AudioBitDepth = 0xde95, + PtpObjectPropertyCode_ScanDepth = 0xde97, + PtpObjectPropertyCode_AudioWaveCodec = 0xde99, + PtpObjectPropertyCode_AudioBitRate = 0xde9a, + PtpObjectPropertyCode_VideoFourCcCodec = 0xde9b, + PtpObjectPropertyCode_VideoBitRate = 0xde9c, + PtpObjectPropertyCode_FramesPerThousandSeconds = 0xde9d, + PtpObjectPropertyCode_KeyFrameDistance = 0xde9e, + PtpObjectPropertyCode_BufferSize = 0xde9f, + PtpObjectPropertyCode_EncodingQuality = 0xdea0, + PtpObjectPropertyCode_EncodingProfile = 0xdea1, + PtpObjectPropertyCode_BuyFlag = 0xd901, + }; + enum PtpDevicePropertyCode : u16 { PtpDevicePropertyCode_Undefined = 0x5000, PtpDevicePropertyCode_BatteryLevel = 0x5001, @@ -264,4 +483,5 @@ namespace haze { u32 parent_object_id; u32 object_id; }; + } diff --git a/troposphere/haze/include/haze/ptp_object_heap.hpp b/troposphere/haze/include/haze/ptp_object_heap.hpp index 54f307094..fe1090a32 100644 --- a/troposphere/haze/include/haze/ptp_object_heap.hpp +++ b/troposphere/haze/include/haze/ptp_object_heap.hpp @@ -119,19 +119,6 @@ namespace haze { /* Otherwise, do nothing. */ /* ... */ } - - template - constexpr T *New(Args&&... args) { - T *t = static_cast(this->Allocate(sizeof(T))); - std::construct_at(t, std::forward(args)...); - return t; - } - - template - constexpr void Delete(T *t) { - std::destroy_at(t); - this->Deallocate(t, sizeof(T)); - } }; } diff --git a/troposphere/haze/include/haze/ptp_responder.hpp b/troposphere/haze/include/haze/ptp_responder.hpp index f38494430..163f7b4af 100644 --- a/troposphere/haze/include/haze/ptp_responder.hpp +++ b/troposphere/haze/include/haze/ptp_responder.hpp @@ -51,7 +51,7 @@ namespace haze { Result WriteResponse(PtpResponseCode code, Data &&data); Result WriteResponse(PtpResponseCode code); - /* Operations. */ + /* PTP operations. */ Result GetDeviceInfo(PtpDataParser &dp); Result OpenSession(PtpDataParser &dp); Result CloseSession(PtpDataParser &dp); @@ -63,6 +63,12 @@ namespace haze { Result SendObjectInfo(PtpDataParser &dp); Result SendObject(PtpDataParser &dp); Result DeleteObject(PtpDataParser &dp); + + /* MTP operations. */ + Result GetObjectPropsSupported(PtpDataParser &dp); + Result GetObjectPropDesc(PtpDataParser &dp); + Result GetObjectPropValue(PtpDataParser &dp); + Result SetObjectPropValue(PtpDataParser &dp); }; } diff --git a/troposphere/haze/include/haze/results.hpp b/troposphere/haze/include/haze/results.hpp index c83132a4a..33bd36ef1 100644 --- a/troposphere/haze/include/haze/results.hpp +++ b/troposphere/haze/include/haze/results.hpp @@ -34,6 +34,7 @@ namespace haze { R_DEFINE_ERROR_RESULT(StorageNotFound, 10); R_DEFINE_ERROR_RESULT(OperationNotSupported, 11); R_DEFINE_ERROR_RESULT(UnknownRequestType, 12); - R_DEFINE_ERROR_RESULT(GeneralFailure, 13); + R_DEFINE_ERROR_RESULT(UnknownPropertyCode, 13); + R_DEFINE_ERROR_RESULT(GeneralFailure, 14); } diff --git a/troposphere/haze/source/event_reactor.cpp b/troposphere/haze/source/event_reactor.cpp index 8cd44d0ac..df3a88e60 100644 --- a/troposphere/haze/source/event_reactor.cpp +++ b/troposphere/haze/source/event_reactor.cpp @@ -49,6 +49,7 @@ namespace haze { } Result EventReactor::WaitForImpl(s32 *out_arg_waiter, const Waiter *arg_waiters, s32 num_arg_waiters) { + HAZE_ASSERT(0 <= num_arg_waiters && num_arg_waiters <= svc::ArgumentHandleCountMax); HAZE_ASSERT(m_num_wait_objects + num_arg_waiters <= svc::ArgumentHandleCountMax); while (true) { diff --git a/troposphere/haze/source/ptp_responder.cpp b/troposphere/haze/source/ptp_responder.cpp index 814af8af0..2cce682cf 100644 --- a/troposphere/haze/source/ptp_responder.cpp +++ b/troposphere/haze/source/ptp_responder.cpp @@ -22,24 +22,24 @@ namespace haze { namespace { constexpr UsbCommsInterfaceInfo MtpInterfaceInfo = { - .bInterfaceClass = 0x06, + .bInterfaceClass = 0x06, .bInterfaceSubClass = 0x01, .bInterfaceProtocol = 0x01, }; /* This is a VID:PID recognized by libmtp. */ - constexpr u16 SwitchMtpIdVendor = 0x057e; + constexpr u16 SwitchMtpIdVendor = 0x057e; constexpr u16 SwitchMtpIdProduct = 0x201d; /* Constants used for MTP GetDeviceInfo response. */ - constexpr u16 MtpStandardVersion = 100; - constexpr u32 MtpVendorExtensionId = 6; - constexpr auto MtpVendorExtensionDesc = "microsoft.com: 1.0;"; + constexpr u16 MtpStandardVersion = 100; + constexpr u32 MtpVendorExtensionId = 6; + constexpr auto MtpVendorExtensionDesc = "microsoft.com: 1.0;"; constexpr u16 MtpFunctionalModeDefault = 0; - constexpr auto MtpDeviceManufacturer = "Nintendo"; - constexpr auto MtpDeviceModel = "Switch"; - constexpr auto MtpDeviceVersion = "1.0.0"; - constexpr auto MtpDeviceSerialNumber = "SerialNumber"; + constexpr auto MtpDeviceManufacturer = "Nintendo"; + constexpr auto MtpDeviceModel = "Switch"; + constexpr auto MtpDeviceVersion = "1.0.0"; + constexpr auto MtpDeviceSerialNumber = "SerialNumber"; enum StorageId : u32 { StorageId_SdmcFs = 0xffffffffu - 1, @@ -57,17 +57,38 @@ namespace haze { PtpOperationCode_SendObjectInfo, PtpOperationCode_SendObject, PtpOperationCode_DeleteObject, + PtpOperationCode_MtpGetObjectPropsSupported, + PtpOperationCode_MtpGetObjectPropDesc, + PtpOperationCode_MtpGetObjectPropValue, + PtpOperationCode_MtpSetObjectPropValue, }; - constexpr const PtpEventCode SupportedEventCodes[] = { /* ... */}; - constexpr const PtpDevicePropertyCode SupportedPropertyCodes[] = { /* ...*/ }; - constexpr const PtpObjectFormatCode SupportedCaptureFormats[] = { /* ...*/ }; + constexpr const PtpEventCode SupportedEventCodes[] = { /* ... */ }; + constexpr const PtpDevicePropertyCode SupportedDeviceProperties[] = { /* ... */ }; + constexpr const PtpObjectFormatCode SupportedCaptureFormats[] = { /* ... */ }; constexpr const PtpObjectFormatCode SupportedPlaybackFormats[] = { PtpObjectFormatCode_Undefined, PtpObjectFormatCode_Association, }; + constexpr const PtpObjectPropertyCode SupportedObjectProperties[] = { + PtpObjectPropertyCode_StorageId, + PtpObjectPropertyCode_ObjectFormat, + PtpObjectPropertyCode_ObjectSize, + PtpObjectPropertyCode_ObjectFileName, + }; + + constexpr bool IsSupportedObjectPropertyCode(PtpObjectPropertyCode c) { + for (size_t i = 0; i < util::size(SupportedObjectProperties); i++) { + if (SupportedObjectProperties[i] == c) { + return true; + } + } + + return false; + } + constexpr const StorageId SupportedStorageIds[] = { StorageId_SdmcFs, }; @@ -141,16 +162,16 @@ namespace haze { constexpr s64 DirectoryReadSize = 32; constexpr u64 FsBufferSize = haze::UsbBulkPacketBufferSize; - constinit char g_filename_str[PtpStringMaxLength + 1]; - constinit char g_capture_date_str[PtpStringMaxLength + 1]; - constinit char g_modification_date_str[PtpStringMaxLength + 1]; - constinit char g_keywords_str[PtpStringMaxLength + 1]; + constinit char g_filename_str[PtpStringMaxLength + 1] = {}; + constinit char g_capture_date_str[PtpStringMaxLength + 1] = {}; + constinit char g_modification_date_str[PtpStringMaxLength + 1] = {}; + constinit char g_keywords_str[PtpStringMaxLength + 1] = {}; constinit FsDirectoryEntry g_dir_entries[DirectoryReadSize] = {}; constinit u8 g_fs_buffer[FsBufferSize] = {}; alignas(4_KB) constinit u8 g_bulk_write_buffer[haze::UsbBulkPacketBufferSize] = {}; - alignas(4_KB) constinit u8 g_bulk_read_buffer[haze::UsbBulkPacketBufferSize] = {}; + alignas(4_KB) constinit u8 g_bulk_read_buffer[haze::UsbBulkPacketBufferSize] = {}; } @@ -190,7 +211,11 @@ namespace haze { R_CATCH(haze::ResultObjectNotFound) { R_TRY(this->WriteResponse(PtpResponseCode_InvalidObjectHandle)); } - R_CATCH(fs::ResultPathNotFound, fs::ResultPathAlreadyExists, fs::ResultTargetLocked, fs::ResultDirectoryNotEmpty, fs::ResultNotEnoughFreeSpaceSdCard) { + R_CATCH(haze::ResultUnknownPropertyCode) { + R_TRY(this->WriteResponse(PtpResponseCode_MtpObjectPropNotSupported)); + } + R_CATCH_MODULE(fs) { + /* Errors from fs are typically recoverable. */ R_TRY(this->WriteResponse(PtpResponseCode_GeneralError)); } R_CATCH_ALL() { @@ -217,18 +242,22 @@ namespace haze { } switch (m_request_header.code) { - case PtpOperationCode_GetDeviceInfo: R_RETURN(this->GetDeviceInfo(dp)); break; - case PtpOperationCode_OpenSession: R_RETURN(this->OpenSession(dp)); break; - case PtpOperationCode_CloseSession: R_RETURN(this->CloseSession(dp)); break; - case PtpOperationCode_GetStorageIds: R_RETURN(this->GetStorageIds(dp)); break; - case PtpOperationCode_GetStorageInfo: R_RETURN(this->GetStorageInfo(dp)); break; - case PtpOperationCode_GetObjectHandles: R_RETURN(this->GetObjectHandles(dp)); break; - case PtpOperationCode_GetObjectInfo: R_RETURN(this->GetObjectInfo(dp)); break; - case PtpOperationCode_GetObject: R_RETURN(this->GetObject(dp)); break; - case PtpOperationCode_SendObjectInfo: R_RETURN(this->SendObjectInfo(dp)); break; - case PtpOperationCode_SendObject: R_RETURN(this->SendObject(dp)); break; - case PtpOperationCode_DeleteObject: R_RETURN(this->DeleteObject(dp)); break; - default: R_THROW(haze::ResultOperationNotSupported()); + case PtpOperationCode_GetDeviceInfo: R_RETURN(this->GetDeviceInfo(dp)); break; + case PtpOperationCode_OpenSession: R_RETURN(this->OpenSession(dp)); break; + case PtpOperationCode_CloseSession: R_RETURN(this->CloseSession(dp)); break; + case PtpOperationCode_GetStorageIds: R_RETURN(this->GetStorageIds(dp)); break; + case PtpOperationCode_GetStorageInfo: R_RETURN(this->GetStorageInfo(dp)); break; + case PtpOperationCode_GetObjectHandles: R_RETURN(this->GetObjectHandles(dp)); break; + case PtpOperationCode_GetObjectInfo: R_RETURN(this->GetObjectInfo(dp)); break; + case PtpOperationCode_GetObject: R_RETURN(this->GetObject(dp)); break; + case PtpOperationCode_SendObjectInfo: R_RETURN(this->SendObjectInfo(dp)); break; + case PtpOperationCode_SendObject: R_RETURN(this->SendObject(dp)); break; + case PtpOperationCode_DeleteObject: R_RETURN(this->DeleteObject(dp)); break; + case PtpOperationCode_MtpGetObjectPropsSupported: R_RETURN(this->GetObjectPropsSupported(dp)); break; + case PtpOperationCode_MtpGetObjectPropDesc: R_RETURN(this->GetObjectPropDesc(dp)); break; + case PtpOperationCode_MtpGetObjectPropValue: R_RETURN(this->GetObjectPropValue(dp)); break; + case PtpOperationCode_MtpSetObjectPropValue: R_RETURN(this->SetObjectPropValue(dp)); break; + default: R_THROW(haze::ResultOperationNotSupported()); } } @@ -264,7 +293,7 @@ namespace haze { R_TRY(db.Add(MtpFunctionalModeDefault)); R_TRY(db.AddArray(SupportedOperationCodes, util::size(SupportedOperationCodes))); R_TRY(db.AddArray(SupportedEventCodes, util::size(SupportedEventCodes))); - R_TRY(db.AddArray(SupportedPropertyCodes, util::size(SupportedPropertyCodes))); + R_TRY(db.AddArray(SupportedDeviceProperties, util::size(SupportedDeviceProperties))); R_TRY(db.AddArray(SupportedCaptureFormats, util::size(SupportedCaptureFormats))); R_TRY(db.AddArray(SupportedPlaybackFormats, util::size(SupportedPlaybackFormats))); R_TRY(db.AddString(MtpDeviceManufacturer)); @@ -329,10 +358,10 @@ namespace haze { R_TRY(m_fs.GetTotalSpace("/", std::addressof(total_space))); R_TRY(m_fs.GetFreeSpace("/", std::addressof(free_space))); - storage_info.max_capacity = total_space; - storage_info.free_space_in_bytes = free_space; + storage_info.max_capacity = total_space; + storage_info.free_space_in_bytes = free_space; storage_info.free_space_in_images = 0; - storage_info.storage_description = "SD Card"; + storage_info.storage_description = "SD Card"; } break; default: @@ -700,4 +729,144 @@ namespace haze { /* We succeeded. */ R_RETURN(this->WriteResponse(PtpResponseCode_Ok)); } + + Result PtpResponder::GetObjectPropsSupported(PtpDataParser &dp) { + R_TRY(dp.Finalize()); + + PtpDataBuilder db(g_bulk_write_buffer, std::addressof(m_usb_server)); + + /* Write information about all object properties we can support. */ + R_TRY(db.WriteVariableLengthData(m_request_header, [&] { + R_RETURN(db.AddArray(SupportedObjectProperties, util::size(SupportedObjectProperties))); + })); + + /* We succeeded. */ + R_RETURN(this->WriteResponse(PtpResponseCode_Ok)); + } + + Result PtpResponder::GetObjectPropDesc(PtpDataParser &dp) { + PtpObjectPropertyCode property_code; + u16 object_format; + + R_TRY(dp.Read(std::addressof(property_code))); + R_TRY(dp.Read(std::addressof(object_format))); + R_TRY(dp.Finalize()); + + /* Ensure we have a valid property code before continuing. */ + R_UNLESS(IsSupportedObjectPropertyCode(property_code), haze::ResultUnknownPropertyCode()); + + /* Begin writing information about the property code. */ + PtpDataBuilder db(g_bulk_write_buffer, std::addressof(m_usb_server)); + + R_TRY(db.WriteVariableLengthData(m_request_header, [&] { + R_TRY(db.Add(property_code)); + + /* Each property code corresponds to a different pattern, which contains the data type, */ + /* whether the property can be set for an object, and the default value of the property. */ + switch (property_code) { + case PtpObjectPropertyCode_ObjectSize: + { + R_TRY(db.Add(PtpDataTypeCode_U64)); + R_TRY(db.Add(PtpPropertyGetSetFlag_Get)); + R_TRY(db.Add(0)); + } + break; + case PtpObjectPropertyCode_StorageId: + { + R_TRY(db.Add(PtpDataTypeCode_U32)); + R_TRY(db.Add(PtpPropertyGetSetFlag_Get)); + R_TRY(db.Add(StorageId_SdmcFs)); + } + break; + case PtpObjectPropertyCode_ObjectFormat: + { + R_TRY(db.Add(PtpDataTypeCode_U32)); + R_TRY(db.Add(PtpPropertyGetSetFlag_Get)); + R_TRY(db.Add(PtpObjectFormatCode_Undefined)); + } + break; + case PtpObjectPropertyCode_ObjectFileName: + { + R_TRY(db.Add(PtpDataTypeCode_String)); + R_TRY(db.Add(PtpPropertyGetSetFlag_GetSet)); + R_TRY(db.AddString("")); + } + break; + HAZE_UNREACHABLE_DEFAULT_CASE(); + } + + /* Group code is a required part of the response, but doesn't seem to be used for anything. */ + R_TRY(db.Add(PtpPropertyGroupCode_Default)); + + /* We don't use the form flag. */ + R_TRY(db.Add(PtpPropertyFormFlag_None)); + + R_SUCCEED(); + })); + + /* We succeeded. */ + R_RETURN(this->WriteResponse(PtpResponseCode_Ok)); + } + + Result PtpResponder::GetObjectPropValue(PtpDataParser &dp) { + u32 object_id; + PtpObjectPropertyCode property_code; + + R_TRY(dp.Read(std::addressof(object_id))); + R_TRY(dp.Read(std::addressof(property_code))); + R_TRY(dp.Finalize()); + + /* Ensure we have a valid property code before continuing. */ + R_UNLESS(IsSupportedObjectPropertyCode(property_code), haze::ResultUnknownPropertyCode()); + + /* Check if we know about the object. If we don't, it's an error. */ + auto * const fileobj = m_object_database.GetObject(object_id); + R_UNLESS(fileobj != nullptr, haze::ResultObjectNotFound()); + + /* Get the object type. */ + FsDirEntryType entry_type; + R_TRY(m_fs.GetEntryType(fileobj->GetName(), std::addressof(entry_type))); + + /* Get the object size. */ + s64 size = 0; + if (entry_type == FsDirEntryType_File) { + FsFile file; + R_TRY(m_fs.OpenFile(fileobj->GetName(), FsOpenMode_Read, std::addressof(file))); + + /* Ensure we maintain a clean state on exit. */ + ON_SCOPE_EXIT { m_fs.CloseFile(std::addressof(file)); }; + + R_TRY(m_fs.GetFileSize(std::addressof(file), std::addressof(size))); + } + + /* Begin writing the requested object property. */ + PtpDataBuilder db(g_bulk_write_buffer, std::addressof(m_usb_server)); + + R_TRY(db.WriteVariableLengthData(m_request_header, [&] { + switch (property_code) { + case PtpObjectPropertyCode_ObjectSize: + R_TRY(db.Add(size)); + break; + case PtpObjectPropertyCode_StorageId: + R_TRY(db.Add(StorageId_SdmcFs)); + break; + case PtpObjectPropertyCode_ObjectFormat: + R_TRY(db.Add(entry_type == FsDirEntryType_File ? PtpObjectFormatCode_Undefined : PtpObjectFormatCode_Association)); + break; + case PtpObjectPropertyCode_ObjectFileName: + R_TRY(db.AddString(std::strrchr(fileobj->GetName(), '/') + 1)); + break; + HAZE_UNREACHABLE_DEFAULT_CASE(); + } + + R_SUCCEED(); + })); + + /* We succeeded. */ + R_RETURN(this->WriteResponse(PtpResponseCode_Ok)); + } + + Result PtpResponder::SetObjectPropValue(PtpDataParser &dp) { + R_THROW(haze::ResultOperationNotSupported()); + } }