/* * Parse file extension and attempt to map it to format and type. Returns true * on success. */ bool impExpImportParseFileExten( CFStringRef fstr, SecExternalFormat *inputFormat, // RETURNED SecExternalItemType *itemType) // RETURNED { if(fstr == NULL) { /* nothing to work with */ return false; } if(CFStringHasSuffix(fstr, CFSTR(".cer")) || CFStringHasSuffix(fstr, CFSTR(".crt"))) { *inputFormat = kSecFormatX509Cert; *itemType = kSecItemTypeCertificate; SecImpInferDbg("Inferring kSecFormatX509Cert from file name"); return true; } if(CFStringHasSuffix(fstr, CFSTR(".p12")) || CFStringHasSuffix(fstr, CFSTR(".pfx"))) { *inputFormat = kSecFormatPKCS12; *itemType = kSecItemTypeAggregate; SecImpInferDbg("Inferring kSecFormatPKCS12 from file name"); return true; } /* Get extension, look for key indicators as substrings */ CFURLRef url = CFURLCreateWithString(NULL, fstr, NULL); if(url == NULL) { SecImpInferDbg("impExpImportParseFileExten: error creating URL"); return false; } CFStringRef exten = CFURLCopyPathExtension(url); CFRelease(url); if(exten == NULL) { /* no extension, app probably passed in only an extension */ exten = fstr; CFRetain(exten); } bool ortn = false; CFRange cfr; cfr = CFStringFind(exten, CFSTR("p7"), kCFCompareCaseInsensitive); if(cfr.length != 0) { *inputFormat = kSecFormatPKCS7; *itemType = kSecItemTypeAggregate; SecImpInferDbg("Inferring kSecFormatPKCS7 from file name"); ortn = true; } if(!ortn) { cfr = CFStringFind(exten, CFSTR("p8"), kCFCompareCaseInsensitive); if(cfr.length != 0) { *inputFormat = kSecFormatWrappedPKCS8; *itemType = kSecItemTypePrivateKey; SecImpInferDbg("Inferring kSecFormatPKCS8 from file name"); ortn = true; } } CFRelease(exten); return ortn; }
SFB::Audio::Metadata::unique_ptr SFB::Audio::Metadata::CreateMetadataForURL(CFURLRef url, CFErrorRef *error) { if(nullptr == url) return nullptr; // If this is a file URL, use the extension-based resolvers SFB::CFString scheme = CFURLCopyScheme(url); // If there is no scheme the URL is invalid if(!scheme) { if(error) *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainPOSIX, EINVAL, nullptr); return nullptr; } if(kCFCompareEqualTo == CFStringCompare(CFSTR("file"), scheme, kCFCompareCaseInsensitive)) { // Verify the file exists SInt32 errorCode = noErr; SFB::CFBoolean fileExists = (CFBooleanRef)CFURLCreatePropertyFromResource(kCFAllocatorDefault, url, kCFURLFileExists, &errorCode); if(fileExists) { if(CFBooleanGetValue(fileExists)) { SFB::CFString pathExtension = CFURLCopyPathExtension(url); if(pathExtension) { // Some extensions (.oga for example) support multiple audio codecs (Vorbis, FLAC, Speex) for(auto subclassInfo : sRegisteredSubclasses) { if(subclassInfo.mHandlesFilesWithExtension(pathExtension)) { unique_ptr metadata(subclassInfo.mCreateMetadata(url)); if(metadata->ReadMetadata(error)) return metadata; } } } } else { LOGGER_WARNING("org.sbooth.AudioEngine.Metadata", "The requested URL doesn't exist"); if(error) { SFB::CFString description = CFCopyLocalizedString(CFSTR("The file “%@” does not exist."), ""); SFB::CFString failureReason = CFCopyLocalizedString(CFSTR("File not found"), ""); SFB::CFString recoverySuggestion = CFCopyLocalizedString(CFSTR("The file may exist on removable media or may have been deleted."), ""); *error = CreateErrorForURL(Metadata::ErrorDomain, Metadata::InputOutputError, description, url, failureReason, recoverySuggestion); } } } else LOGGER_WARNING("org.sbooth.AudioEngine.Metadata", "CFURLCreatePropertyFromResource failed: " << errorCode); } return nullptr; }
//----------------------------------------------------------------------------- bool HasExtension(CFURLRef fileUrl, CFStringRef ext) { CFStringRef fileExt; bool ok; fileExt = CFURLCopyPathExtension(fileUrl); if (fileExt) { ok = (kCFCompareEqualTo == CFStringCompare(fileExt, ext, kCFCompareCaseInsensitive)); CFRelease(fileExt); return ok; } return false; }
CF::String URL::GetPathExtension( void ) { CF::String str; CFStringRef cfStr; if( this->_cfObject == NULL ) { return str; } cfStr = CFURLCopyPathExtension( this->_cfObject ); if( cfStr != NULL ) { str = cfStr; CFRelease( cfStr ); } return str; }
/* * Map a filename and/or a BIFileType to a UTI string. */ static CFStringRef BIGetUTI( CFURLRef url, BIFileType fileType) { switch(fileType) { case BI_FT_JPEG: case BI_FT_JPEG_Lossless: return kUTTypeJPEG; case BI_FT_TIFF: return kUTTypeTIFF; case BI_FT_JPEG2000: case BI_FT_JPEG2000_Lossless: return kUTTypeJPEG2000; case BI_FT_Default: /* figure it out from file extension */ break; default: fprintf(stderr, "***BIGetUTI internal error\n"); return NULL; } CFStringRef fileExt = CFURLCopyPathExtension(url); if(fileExt == NULL) { fprintf(stderr, "***BIGetUTI(BI_FT_Default) - no extension available\n"); return NULL; } for(unsigned mapDex=0; mapDex<NUM_UTI_MAPS; mapDex++) { const UTIMap *map = &utiMap[mapDex]; if(CFEqual(fileExt, map->exten)) { return map->uti; } } fprintf(stderr, "***BIGetUTI(BI_FT_Default) - unknown extension\n"); return NULL; }
AudioDecoder * AudioDecoder::CreateDecoderForInputSource(InputSource *inputSource, CFStringRef mimeType, CFErrorRef *error) { if(NULL == inputSource) return NULL; AudioDecoder *decoder = NULL; // Open the input source if it isn't already if(AutomaticallyOpenDecoders() && !inputSource->IsOpen() && !inputSource->Open(error)) return NULL; // As a factory this class has knowledge of its subclasses // It would be possible (and perhaps preferable) to switch to a generic // plugin interface at a later date #if 0 // If the input is an instance of HTTPInputSource, use the MIME type from the server // This code is disabled because most HTTP servers don't send the correct MIME types HTTPInputSource *httpInputSource = dynamic_cast<HTTPInputSource *>(inputSource); bool releaseMIMEType = false; if(!mimeType && httpInputSource && httpInputSource->IsOpen()) { mimeType = httpInputSource->CopyContentMIMEType(); if(mimeType) releaseMIMEType = true; } #endif // The MIME type takes precedence over the file extension if(mimeType) { #if BUILD_FOR_MAC_OSX if(FLACDecoder::HandlesMIMEType(mimeType)) { decoder = new FLACDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = NULL; delete decoder, decoder = NULL; } } if(NULL == decoder && WavPackDecoder::HandlesMIMEType(mimeType)) { decoder = new WavPackDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = NULL; delete decoder, decoder = NULL; } } if(NULL == decoder && MPEGDecoder::HandlesMIMEType(mimeType)) { decoder = new MPEGDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = NULL; delete decoder, decoder = NULL; } } if(NULL == decoder && OggVorbisDecoder::HandlesMIMEType(mimeType)) { decoder = new OggVorbisDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = NULL; delete decoder, decoder = NULL; } } if(NULL == decoder && MusepackDecoder::HandlesMIMEType(mimeType)) { decoder = new MusepackDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = NULL; delete decoder, decoder = NULL; } } if(NULL == decoder && MonkeysAudioDecoder::HandlesMIMEType(mimeType)) { decoder = new MonkeysAudioDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = NULL; delete decoder, decoder = NULL; } } if(NULL == decoder && OggSpeexDecoder::HandlesMIMEType(mimeType)) { decoder = new OggSpeexDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = NULL; delete decoder, decoder = NULL; } } if(NULL == decoder && MODDecoder::HandlesMIMEType(mimeType)) { decoder = new MODDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = NULL; delete decoder, decoder = NULL; } } if(NULL == decoder && LibsndfileDecoder::HandlesMIMEType(mimeType)) { decoder = new LibsndfileDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = NULL; delete decoder, decoder = NULL; } } #endif if(NULL == decoder && CoreAudioDecoder::HandlesMIMEType(mimeType)) { decoder = new CoreAudioDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = NULL; delete decoder, decoder = NULL; } } #if 0 if(releaseMIMEType) CFRelease(mimeType), mimeType = NULL; #endif if(decoder) return decoder; } // If no MIME type was specified, use the extension-based resolvers CFURLRef inputURL = inputSource->GetURL(); if(!inputURL) return NULL; CFStringRef pathExtension = CFURLCopyPathExtension(inputURL); if(!pathExtension) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 32, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CFURLCopyLastPathComponent(inputURL); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The type of the file “%@” could not be determined."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Unknown file type"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may be missing or may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, InputSourceErrorDomain, InputSourceFileNotFoundError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } return NULL; } // TODO: Some extensions (.oga for example) support multiple audio codecs (Vorbis, FLAC, Speex) // and if openDecoder is false the wrong decoder type may be returned, since the file isn't analyzed // until Open() is called #if BUILD_FOR_MAC_OSX if(FLACDecoder::HandlesFilesWithExtension(pathExtension)) { decoder = new FLACDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = NULL; delete decoder, decoder = NULL; } } if(NULL == decoder && WavPackDecoder::HandlesFilesWithExtension(pathExtension)) { decoder = new WavPackDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = NULL; delete decoder, decoder = NULL; } } if(NULL == decoder && MPEGDecoder::HandlesFilesWithExtension(pathExtension)) { decoder = new MPEGDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = NULL; delete decoder, decoder = NULL; } } if(NULL == decoder && OggVorbisDecoder::HandlesFilesWithExtension(pathExtension)) { decoder = new OggVorbisDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = NULL; delete decoder, decoder = NULL; } } if(NULL == decoder && MusepackDecoder::HandlesFilesWithExtension(pathExtension)) { decoder = new MusepackDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = NULL; delete decoder, decoder = NULL; } } if(NULL == decoder && MonkeysAudioDecoder::HandlesFilesWithExtension(pathExtension)) { decoder = new MonkeysAudioDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = NULL; delete decoder, decoder = NULL; } } if(NULL == decoder && OggSpeexDecoder::HandlesFilesWithExtension(pathExtension)) { decoder = new OggSpeexDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = NULL; delete decoder, decoder = NULL; } } if(NULL == decoder && MODDecoder::HandlesFilesWithExtension(pathExtension)) { decoder = new MODDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = NULL; delete decoder, decoder = NULL; } } if(NULL == decoder && LibsndfileDecoder::HandlesFilesWithExtension(pathExtension)) { decoder = new LibsndfileDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = NULL; delete decoder, decoder = NULL; } } #endif if(NULL == decoder && CoreAudioDecoder::HandlesFilesWithExtension(pathExtension)) { decoder = new CoreAudioDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = NULL; delete decoder, decoder = NULL; } } CFRelease(pathExtension), pathExtension = NULL; return decoder; }
bool SFB::Audio::MODMetadata::_ReadMetadata(CFErrorRef *error) { UInt8 buf [PATH_MAX]; if(!CFURLGetFileSystemRepresentation(mURL, false, buf, PATH_MAX)) return false; SFB::CFString pathExtension = CFURLCopyPathExtension(mURL); if(!pathExtension) return false; bool fileIsValid = false; if(kCFCompareEqualTo == CFStringCompare(pathExtension, CFSTR("it"), kCFCompareCaseInsensitive)) { std::unique_ptr<TagLib::FileStream> stream(new TagLib::FileStream((const char *)buf, true)); if(!stream->isOpen()) { if(error) { SFB::CFString description = CFCopyLocalizedString(CFSTR("The file “%@” could not be opened for reading."), ""); SFB::CFString failureReason = CFCopyLocalizedString(CFSTR("Input/output error"), ""); SFB::CFString recoverySuggestion = CFCopyLocalizedString(CFSTR("The file may have been renamed, moved, deleted, or you may not have appropriate permissions."), ""); *error = CreateErrorForURL(Metadata::ErrorDomain, Metadata::InputOutputError, description, mURL, failureReason, recoverySuggestion); } return false; } TagLib::IT::File file(stream.get()); if(file.isValid()) { fileIsValid = true; CFDictionarySetValue(mMetadata, kFormatNameKey, CFSTR("MOD (Impulse Tracker)")); if(file.audioProperties()) AddAudioPropertiesToDictionary(mMetadata, file.audioProperties()); if(file.tag()) AddTagToDictionary(mMetadata, file.tag()); } } else if(kCFCompareEqualTo == CFStringCompare(pathExtension, CFSTR("xm"), kCFCompareCaseInsensitive)) { std::unique_ptr<TagLib::FileStream> stream(new TagLib::FileStream((const char *)buf, true)); if(!stream->isOpen()) { if(error) { SFB::CFString description = CFCopyLocalizedString(CFSTR("The file “%@” could not be opened for reading."), ""); SFB::CFString failureReason = CFCopyLocalizedString(CFSTR("Input/output error"), ""); SFB::CFString recoverySuggestion = CFCopyLocalizedString(CFSTR("The file may have been renamed, moved, deleted, or you may not have appropriate permissions."), ""); *error = CreateErrorForURL(Metadata::ErrorDomain, Metadata::InputOutputError, description, mURL, failureReason, recoverySuggestion); } return false; } TagLib::XM::File file(stream.get()); if(file.isValid()) { fileIsValid = true; CFDictionarySetValue(mMetadata, kFormatNameKey, CFSTR("MOD (Extended Module)")); if(file.audioProperties()) AddAudioPropertiesToDictionary(mMetadata, file.audioProperties()); if(file.tag()) AddTagToDictionary(mMetadata, file.tag()); } } else if(kCFCompareEqualTo == CFStringCompare(pathExtension, CFSTR("s3m"), kCFCompareCaseInsensitive)) { std::unique_ptr<TagLib::FileStream> stream(new TagLib::FileStream((const char *)buf, true)); if(!stream->isOpen()) { if(error) { SFB::CFString description = CFCopyLocalizedString(CFSTR("The file “%@” could not be opened for reading."), ""); SFB::CFString failureReason = CFCopyLocalizedString(CFSTR("Input/output error"), ""); SFB::CFString recoverySuggestion = CFCopyLocalizedString(CFSTR("The file may have been renamed, moved, deleted, or you may not have appropriate permissions."), ""); *error = CreateErrorForURL(Metadata::ErrorDomain, Metadata::InputOutputError, description, mURL, failureReason, recoverySuggestion); } return false; } TagLib::S3M::File file(stream.get()); if(file.isValid()) { fileIsValid = true; CFDictionarySetValue(mMetadata, kFormatNameKey, CFSTR("MOD (ScreamTracker III)")); if(file.audioProperties()) AddAudioPropertiesToDictionary(mMetadata, file.audioProperties()); if(file.tag()) AddTagToDictionary(mMetadata, file.tag()); } } else if(kCFCompareEqualTo == CFStringCompare(pathExtension, CFSTR("mod"), kCFCompareCaseInsensitive)) { std::unique_ptr<TagLib::FileStream> stream(new TagLib::FileStream((const char *)buf, true)); if(!stream->isOpen()) { if(error) { SFB::CFString description = CFCopyLocalizedString(CFSTR("The file “%@” could not be opened for reading."), ""); SFB::CFString failureReason = CFCopyLocalizedString(CFSTR("Input/output error"), ""); SFB::CFString recoverySuggestion = CFCopyLocalizedString(CFSTR("The file may have been renamed, moved, deleted, or you may not have appropriate permissions."), ""); *error = CreateErrorForURL(Metadata::ErrorDomain, Metadata::InputOutputError, description, mURL, failureReason, recoverySuggestion); } return false; } TagLib::Mod::File file(stream.get()); if(file.isValid()) { fileIsValid = true; CFDictionarySetValue(mMetadata, kFormatNameKey, CFSTR("MOD (Protracker)")); if(file.audioProperties()) AddAudioPropertiesToDictionary(mMetadata, file.audioProperties()); if(file.tag()) AddTagToDictionary(mMetadata, file.tag()); } } if(!fileIsValid) { if(error) { SFB::CFString description = CFCopyLocalizedString(CFSTR("The file “%@” is not a valid MOD file."), ""); SFB::CFString failureReason = CFCopyLocalizedString(CFSTR("Not a MOD file"), ""); SFB::CFString recoverySuggestion = CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), ""); *error = CreateErrorForURL(Metadata::ErrorDomain, Metadata::InputOutputError, description, mURL, failureReason, recoverySuggestion); } return false; } return true; }
string URL::extension() const { return mkstr(CFURLCopyPathExtension(ref)); }
bool MODMetadata::ReadMetadata(CFErrorRef *error) { // Start from scratch CFDictionaryRemoveAllValues(mMetadata); CFDictionaryRemoveAllValues(mChangedMetadata); UInt8 buf [PATH_MAX]; if(!CFURLGetFileSystemRepresentation(mURL, false, buf, PATH_MAX)) return false; CFStringRef pathExtension = CFURLCopyPathExtension(mURL); if(nullptr == pathExtension) return false; bool fileIsValid = false; if(kCFCompareEqualTo == CFStringCompare(pathExtension, CFSTR("it"), kCFCompareCaseInsensitive)) { auto stream = new TagLib::FileStream(reinterpret_cast<const char *>(buf), true); TagLib::IT::File file(stream); if(file.isValid()) { fileIsValid = true; CFDictionarySetValue(mMetadata, kPropertiesFormatNameKey, CFSTR("MOD (Impulse Tracker)")); if(file.audioProperties()) AddAudioPropertiesToDictionary(mMetadata, file.audioProperties()); if(file.tag()) AddTagToDictionary(mMetadata, file.tag()); } } else if(kCFCompareEqualTo == CFStringCompare(pathExtension, CFSTR("xm"), kCFCompareCaseInsensitive)) { auto stream = new TagLib::FileStream(reinterpret_cast<const char *>(buf), true); TagLib::XM::File file(stream); if(file.isValid()) { fileIsValid = true; CFDictionarySetValue(mMetadata, kPropertiesFormatNameKey, CFSTR("MOD (Extended Module)")); if(file.audioProperties()) AddAudioPropertiesToDictionary(mMetadata, file.audioProperties()); if(file.tag()) AddTagToDictionary(mMetadata, file.tag()); } } else if(kCFCompareEqualTo == CFStringCompare(pathExtension, CFSTR("s3m"), kCFCompareCaseInsensitive)) { auto stream = new TagLib::FileStream(reinterpret_cast<const char *>(buf), true); TagLib::S3M::File file(stream); if(file.isValid()) { fileIsValid = true; CFDictionarySetValue(mMetadata, kPropertiesFormatNameKey, CFSTR("MOD (ScreamTracker III)")); if(file.audioProperties()) AddAudioPropertiesToDictionary(mMetadata, file.audioProperties()); if(file.tag()) AddTagToDictionary(mMetadata, file.tag()); } } else if(kCFCompareEqualTo == CFStringCompare(pathExtension, CFSTR("mod"), kCFCompareCaseInsensitive)) { auto stream = new TagLib::FileStream(reinterpret_cast<const char *>(buf), true); TagLib::Mod::File file(stream); if(file.isValid()) { fileIsValid = true; CFDictionarySetValue(mMetadata, kPropertiesFormatNameKey, CFSTR("MOD (Protracker)")); if(file.audioProperties()) AddAudioPropertiesToDictionary(mMetadata, file.audioProperties()); if(file.tag()) AddTagToDictionary(mMetadata, file.tag()); } } CFRelease(pathExtension), pathExtension = nullptr; if(!fileIsValid) { if(error) { CFStringRef description = CFCopyLocalizedString(CFSTR("The file “%@” is not a valid MOD file."), ""); CFStringRef failureReason = CFCopyLocalizedString(CFSTR("Not a MOD file"), ""); CFStringRef recoverySuggestion = CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), ""); *error = CreateErrorForURL(AudioMetadataErrorDomain, AudioMetadataInputOutputError, description, mURL, failureReason, recoverySuggestion); CFRelease(description), description = nullptr; CFRelease(failureReason), failureReason = nullptr; CFRelease(recoverySuggestion), recoverySuggestion = nullptr; } return false; } return true; }
AudioDecoder * AudioDecoder::CreateDecoderForInputSource(InputSource *inputSource, CFStringRef mimeType, CFErrorRef *error) { if(nullptr == inputSource) return nullptr; AudioDecoder *decoder = nullptr; // Open the input source if it isn't already if(AutomaticallyOpenDecoders() && !inputSource->IsOpen() && !inputSource->Open(error)) return nullptr; // As a factory this class has knowledge of its subclasses // It would be possible (and perhaps preferable) to switch to a generic // plugin interface at a later date #if 0 // If the input is an instance of HTTPInputSource, use the MIME type from the server // This code is disabled because most HTTP servers don't send the correct MIME types HTTPInputSource *httpInputSource = dynamic_cast<HTTPInputSource *>(inputSource); bool releaseMIMEType = false; if(!mimeType && httpInputSource && httpInputSource->IsOpen()) { mimeType = httpInputSource->CopyContentMIMEType(); if(mimeType) releaseMIMEType = true; } #endif // The MIME type takes precedence over the file extension if(mimeType) { if(FLACDecoder::HandlesMIMEType(mimeType)) { decoder = new FLACDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } if(nullptr == decoder && WavPackDecoder::HandlesMIMEType(mimeType)) { decoder = new WavPackDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } if(nullptr == decoder && MPEGDecoder::HandlesMIMEType(mimeType)) { decoder = new MPEGDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } if(nullptr == decoder && OggVorbisDecoder::HandlesMIMEType(mimeType)) { decoder = new OggVorbisDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } if(nullptr == decoder && OggSpeexDecoder::HandlesMIMEType(mimeType)) { decoder = new OggSpeexDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } #if !TARGET_OS_IPHONE if(nullptr == decoder && MusepackDecoder::HandlesMIMEType(mimeType)) { decoder = new MusepackDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } if(nullptr == decoder && MonkeysAudioDecoder::HandlesMIMEType(mimeType)) { decoder = new MonkeysAudioDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } if(nullptr == decoder && MODDecoder::HandlesMIMEType(mimeType)) { decoder = new MODDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } if(nullptr == decoder && TrueAudioDecoder::HandlesMIMEType(mimeType)) { decoder = new TrueAudioDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } if(nullptr == decoder && LibsndfileDecoder::HandlesMIMEType(mimeType)) { decoder = new LibsndfileDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } #endif if(nullptr == decoder && CoreAudioDecoder::HandlesMIMEType(mimeType)) { decoder = new CoreAudioDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } #if 0 if(releaseMIMEType) CFRelease(mimeType), mimeType = nullptr; #endif if(decoder) return decoder; } // If no MIME type was specified, use the extension-based resolvers CFURLRef inputURL = inputSource->GetURL(); if(!inputURL) return nullptr; // Determining the extension isn't as simple as using CFURLCopyPathExtension (wouldn't that be nice?), // because although the behavior on Lion works like one would expect, on Snow Leopard it returns // a number that I believe is part of the inode number, but is definitely NOT the extension CFStringRef pathExtension = nullptr; #if !TARGET_OS_IPHONE CFURLRef filePathURL = CFURLCreateFilePathURL(kCFAllocatorDefault, inputURL, nullptr); if(filePathURL) { pathExtension = CFURLCopyPathExtension(filePathURL); CFRelease(filePathURL), filePathURL = nullptr; } else #endif pathExtension = CFURLCopyPathExtension(inputURL); if(!pathExtension) { if(error) { CFStringRef description = CFCopyLocalizedString(CFSTR("The type of the file “%@” could not be determined."), ""); CFStringRef failureReason = CFCopyLocalizedString(CFSTR("Unknown file type"), ""); CFStringRef recoverySuggestion = CFCopyLocalizedString(CFSTR("The file's extension may be missing or may not match the file's type."), ""); *error = CreateErrorForURL(InputSourceErrorDomain, InputSourceFileNotFoundError, description, inputURL, failureReason, recoverySuggestion); CFRelease(description), description = nullptr; CFRelease(failureReason), failureReason = nullptr; CFRelease(recoverySuggestion), recoverySuggestion = nullptr; } return nullptr; } // TODO: Some extensions (.oga for example) support multiple audio codecs (Vorbis, FLAC, Speex) // and if openDecoder is false the wrong decoder type may be returned, since the file isn't analyzed // until Open() is called if(FLACDecoder::HandlesFilesWithExtension(pathExtension)) { decoder = new FLACDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } if(nullptr == decoder && WavPackDecoder::HandlesFilesWithExtension(pathExtension)) { decoder = new WavPackDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } if(nullptr == decoder && MPEGDecoder::HandlesFilesWithExtension(pathExtension)) { decoder = new MPEGDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } if(nullptr == decoder && OggVorbisDecoder::HandlesFilesWithExtension(pathExtension)) { decoder = new OggVorbisDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } if(nullptr == decoder && OggSpeexDecoder::HandlesFilesWithExtension(pathExtension)) { decoder = new OggSpeexDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } #if !TARGET_OS_IPHONE if(nullptr == decoder && MusepackDecoder::HandlesFilesWithExtension(pathExtension)) { decoder = new MusepackDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } if(nullptr == decoder && MonkeysAudioDecoder::HandlesFilesWithExtension(pathExtension)) { decoder = new MonkeysAudioDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } if(nullptr == decoder && MODDecoder::HandlesFilesWithExtension(pathExtension)) { decoder = new MODDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } if(nullptr == decoder && TrueAudioDecoder::HandlesFilesWithExtension(pathExtension)) { decoder = new TrueAudioDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } if(nullptr == decoder && LibsndfileDecoder::HandlesFilesWithExtension(pathExtension)) { decoder = new LibsndfileDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } #endif if(nullptr == decoder && CoreAudioDecoder::HandlesFilesWithExtension(pathExtension)) { decoder = new CoreAudioDecoder(inputSource); if(AutomaticallyOpenDecoders() && !decoder->Open(error)) { decoder->mInputSource = nullptr; delete decoder, decoder = nullptr; } } CFRelease(pathExtension), pathExtension = nullptr; return decoder; }
SFB::Audio::Decoder::unique_ptr SFB::Audio::Decoder::CreateDecoderForInputSource(InputSource::unique_ptr inputSource, CFStringRef mimeType, CFErrorRef *error) { if(!inputSource) return nullptr; // Open the input source if it isn't already if(AutomaticallyOpenDecoders() && !inputSource->IsOpen() && !inputSource->Open(error)) return nullptr; #if 0 // If the input is an instance of HTTPInputSource, use the MIME type from the server // This code is disabled because most HTTP servers don't send the correct MIME types HTTPInputSource *httpInputSource = dynamic_cast<HTTPInputSource *>(inputSource); bool releaseMIMEType = false; if(!mimeType && httpInputSource && httpInputSource->IsOpen()) { mimeType = httpInputSource->CopyContentMIMEType(); if(mimeType) releaseMIMEType = true; } #endif // The MIME type takes precedence over the file extension if(mimeType) { for(auto subclassInfo : sRegisteredSubclasses) { if(subclassInfo.mHandlesMIMEType(mimeType)) { unique_ptr decoder(subclassInfo.mCreateDecoder(std::move(inputSource))); if(!AutomaticallyOpenDecoders()) return decoder; else { if(decoder->Open(error)) return decoder; // Take back the input source for reuse if opening fails else inputSource = std::move(decoder->mInputSource); } } } #if 0 if(releaseMIMEType) CFRelease(mimeType), mimeType = nullptr; #endif } // If no MIME type was specified, use the extension-based resolvers CFURLRef inputURL = inputSource->GetURL(); if(!inputURL) return nullptr; SFB::CFString pathExtension = CFURLCopyPathExtension(inputURL); if(!pathExtension) { if(error) { SFB::CFString description = CFCopyLocalizedString(CFSTR("The type of the file “%@” could not be determined."), ""); SFB::CFString failureReason = CFCopyLocalizedString(CFSTR("Unknown file type"), ""); SFB::CFString recoverySuggestion = CFCopyLocalizedString(CFSTR("The file's extension may be missing or may not match the file's type."), ""); *error = CreateErrorForURL(InputSource::ErrorDomain, InputSource::FileNotFoundError, description, inputURL, failureReason, recoverySuggestion); } return nullptr; } // TODO: Some extensions (.oga for example) support multiple audio codecs (Vorbis, FLAC, Speex) // and if openDecoder is false the wrong decoder type may be returned, since the file isn't analyzed // until Open() is called for(auto subclassInfo : sRegisteredSubclasses) { if(subclassInfo.mHandlesFilesWithExtension(pathExtension)) { unique_ptr decoder(subclassInfo.mCreateDecoder(std::move(inputSource))); if(!AutomaticallyOpenDecoders()) return decoder; else { if(decoder->Open(error)) return decoder; // Take back the input source for reuse if opening fails else inputSource = std::move(decoder->mInputSource); } } } return nullptr; }
bool SFB::Audio::MODDecoder::_Open(CFErrorRef *error) { dfs.open = nullptr; dfs.skip = skip_callback; dfs.getc = getc_callback; dfs.getnc = getnc_callback; dfs.close = close_callback; df = unique_DUMBFILE_ptr(dumbfile_open_ex(this, &dfs), dumbfile_close); if(!df) { return false; } SFB::CFString pathExtension = CFURLCopyPathExtension(GetURL()); if(nullptr == pathExtension) return false; // Attempt to create the appropriate decoder based on the file's extension if(kCFCompareEqualTo == CFStringCompare(pathExtension, CFSTR("it"), kCFCompareCaseInsensitive)) duh = unique_DUH_ptr(dumb_read_it(df.get()), unload_duh); else if(kCFCompareEqualTo == CFStringCompare(pathExtension, CFSTR("xm"), kCFCompareCaseInsensitive)) duh = unique_DUH_ptr(dumb_read_xm(df.get()), unload_duh); else if(kCFCompareEqualTo == CFStringCompare(pathExtension, CFSTR("s3m"), kCFCompareCaseInsensitive)) duh = unique_DUH_ptr(dumb_read_s3m(df.get()), unload_duh); else if(kCFCompareEqualTo == CFStringCompare(pathExtension, CFSTR("mod"), kCFCompareCaseInsensitive)) duh = unique_DUH_ptr(dumb_read_mod(df.get()), unload_duh); if(!duh) { if(error) { SFB::CFString description = CFCopyLocalizedString(CFSTR("The file “%@” is not a valid MOD file."), ""); SFB::CFString failureReason = CFCopyLocalizedString(CFSTR("Not a MOD file"), ""); SFB::CFString recoverySuggestion = CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), ""); *error = CreateErrorForURL(Decoder::ErrorDomain, Decoder::InputOutputError, description, mInputSource->GetURL(), failureReason, recoverySuggestion); } return false; } // NB: This must change if the sample rate changes because it is based on 65536 Hz mTotalFrames = duh_get_length(duh.get()); dsr = unique_DUH_SIGRENDERER_ptr(duh_start_sigrenderer(duh.get(), 0, 2, 0), duh_end_sigrenderer); if(!dsr) { if(error) { SFB::CFString description = CFCopyLocalizedString(CFSTR("The file “%@” is not a valid MOD file."), ""); SFB::CFString failureReason = CFCopyLocalizedString(CFSTR("Not a MOD file"), ""); SFB::CFString recoverySuggestion = CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), ""); *error = CreateErrorForURL(Decoder::ErrorDomain, Decoder::InputOutputError, description, mInputSource->GetURL(), failureReason, recoverySuggestion); } return false; } // Generate interleaved 2 channel 44.1 16-bit output mFormat.mFormatID = kAudioFormatLinearPCM; mFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; mFormat.mSampleRate = DUMB_SAMPLE_RATE; mFormat.mChannelsPerFrame = DUMB_CHANNELS; mFormat.mBitsPerChannel = DUMB_BIT_DEPTH; mFormat.mBytesPerPacket = (mFormat.mBitsPerChannel / 8) * mFormat.mChannelsPerFrame; mFormat.mFramesPerPacket = 1; mFormat.mBytesPerFrame = mFormat.mBytesPerPacket * mFormat.mFramesPerPacket; mFormat.mReserved = 0; // Set up the source format mSourceFormat.mFormatID = 'MOD '; mSourceFormat.mSampleRate = DUMB_SAMPLE_RATE; mSourceFormat.mChannelsPerFrame = DUMB_CHANNELS; // Setup the channel layout mChannelLayout = ChannelLayout::ChannelLayoutWithTag(kAudioChannelLayoutTag_Stereo); return true; }
ExitStatus readArgs( int * argc, char * const ** argv, KcgenArgs * toolArgs) { ExitStatus result = EX_USAGE; ExitStatus scratchResult = EX_USAGE; CFStringRef scratchString = NULL; // must release CFNumberRef scratchNumber = NULL; // must release CFURLRef scratchURL = NULL; // must release size_t len = 0; int32_t i = 0; int optchar = 0; int longindex = -1; bzero(toolArgs, sizeof(*toolArgs)); /***** * Allocate collection objects. */ if (!createCFMutableSet(&toolArgs->kextIDs, &kCFTypeSetCallBacks) || !createCFMutableSet(&toolArgs->optionalKextIDs, &kCFTypeSetCallBacks) || !createCFMutableArray(&toolArgs->argURLs, &kCFTypeArrayCallBacks) || !createCFMutableArray(&toolArgs->repositoryURLs, &kCFTypeArrayCallBacks) || !createCFMutableArray(&toolArgs->namedKextURLs, &kCFTypeArrayCallBacks) || !createCFMutableArray(&toolArgs->targetArchs, NULL)) { OSKextLogMemError(); result = EX_OSERR; exit(result); } /***** * Process command line arguments. */ while ((optchar = getopt_long_only(*argc, *argv, kOptChars, sOptInfo, &longindex)) != -1) { SAFE_RELEASE_NULL(scratchString); SAFE_RELEASE_NULL(scratchNumber); SAFE_RELEASE_NULL(scratchURL); /* When processing short (single-char) options, there is no way to * express optional arguments. Instead, we suppress missing option * argument errors by adding a leading ':' to the option string. * When getopt detects a missing argument, it will return a ':' so that * we can screen for options that are not required to have an argument. */ if (optchar == ':') { switch (optopt) { case kOptPrelinkedKernel: optchar = optopt; break; default: OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Error - option requires an argument -- -%c.", optopt); break; } } switch (optchar) { case kOptArch: if (!addArchForName(toolArgs, optarg)) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Error - unknown architecture %s.", optarg); goto finish; } break; case kOptBundleIdentifier: scratchString = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8); if (!scratchString) { OSKextLogMemError(); result = EX_OSERR; goto finish; } CFSetAddValue(toolArgs->kextIDs, scratchString); break; case kOptPrelinkedKernel: scratchResult = readPrelinkedKernelArgs(toolArgs, *argc, *argv, /* isLongopt */ longindex != -1); if (scratchResult != EX_OK) { result = scratchResult; goto finish; } break; case kOptHelp: usage(kUsageLevelFull); result = kKcgenExitHelp; goto finish; case kOptKernel: if (toolArgs->kernelPath) { OSKextLog(/* kext */ NULL, kOSKextLogWarningLevel | kOSKextLogGeneralFlag, "Warning - kernel file already specified; using last."); } else { toolArgs->kernelPath = malloc(PATH_MAX); if (!toolArgs->kernelPath) { OSKextLogMemError(); result = EX_OSERR; goto finish; } } len = strlcpy(toolArgs->kernelPath, optarg, PATH_MAX); if (len >= PATH_MAX) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Error - kernel filename length exceeds PATH_MAX"); goto finish; } break; case kOptTests: toolArgs->printTestResults = true; break; case kOptQuiet: beQuiet(); break; case kOptVerbose: scratchResult = setLogFilterForOpt(*argc, *argv, /* forceOnFlags */ kOSKextLogKextOrGlobalMask); if (scratchResult != EX_OK) { result = scratchResult; goto finish; } break; case kOptNoAuthentication: OSKextLog(/* kext */ NULL, kOSKextLogWarningLevel | kOSKextLogGeneralFlag, "Note: -%s is implicitly set for %s.", kOptNameNoAuthentication, progname); break; case 0: switch (longopt) { case kLongOptOptionalBundleIdentifier: scratchString = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8); if (!scratchString) { OSKextLogMemError(); result = EX_OSERR; goto finish; } CFSetAddValue(toolArgs->optionalKextIDs, scratchString); break; case kLongOptCompressed: toolArgs->compress = true; break; case kLongOptUncompressed: toolArgs->uncompress = true; break; case kLongOptSymbols: if (toolArgs->symbolDirURL) { OSKextLog(/* kext */ NULL, kOSKextLogWarningLevel | kOSKextLogGeneralFlag, "Warning - symbol directory already specified; using last."); SAFE_RELEASE_NULL(toolArgs->symbolDirURL); } scratchURL = CFURLCreateFromFileSystemRepresentation( kCFAllocatorDefault, (const UInt8 *)optarg, strlen(optarg), true); if (!scratchURL) { OSKextLogStringError(/* kext */ NULL); result = EX_OSERR; goto finish; } toolArgs->symbolDirURL = CFRetain(scratchURL); toolArgs->generatePrelinkedSymbols = true; break; case kLongOptVolumeRoot: if (toolArgs->volumeRootURL) { OSKextLog(/* kext */ NULL, kOSKextLogWarningLevel | kOSKextLogGeneralFlag, "Warning: volume root already specified; using last."); SAFE_RELEASE_NULL(toolArgs->volumeRootURL); } scratchURL = CFURLCreateFromFileSystemRepresentation( kCFAllocatorDefault, (const UInt8 *)optarg, strlen(optarg), true); if (!scratchURL) { OSKextLogStringError(/* kext */ NULL); result = EX_OSERR; goto finish; } toolArgs->volumeRootURL = CFRetain(scratchURL); break; case kLongOptAllPersonalities: OSKextLog(/* kext */ NULL, kOSKextLogWarningLevel | kOSKextLogGeneralFlag, "Note: -%s is implicitly set for %s.", kOptNameAllPersonalities, progname); break; case kLongOptNoLinkFailures: OSKextLog(/* kext */ NULL, kOSKextLogWarningLevel | kOSKextLogGeneralFlag, "Note: -%s is implicitly set for %s.", kOptNameNoLinkFailures, progname); break; case kLongOptStripSymbols: toolArgs->stripSymbols = true; break; case kLongOptMaxSliceSize: toolArgs->maxSliceSize = atol(optarg); break; default: /* Because we use ':', getopt_long doesn't print an error message. */ OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Error - unrecognized option %s", (*argv)[optind-1]); goto finish; break; } break; default: /* Because we use ':', getopt_long doesn't print an error message. */ OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Error - unrecognized option %s", (*argv)[optind-1]); goto finish; break; } /* Reset longindex, because getopt_long_only() is stupid and doesn't. */ longindex = -1; } /* Update the argc & argv seen by main(). */ *argc -= optind; *argv += optind; /* * Record the kext & directory names from the command line. */ for (i = 0; i < *argc; i++) { SAFE_RELEASE_NULL(scratchURL); SAFE_RELEASE_NULL(scratchString); scratchURL = CFURLCreateFromFileSystemRepresentation( kCFAllocatorDefault, (const UInt8 *)(*argv)[i], strlen((*argv)[i]), true); if (!scratchURL) { OSKextLogMemError(); result = EX_OSERR; goto finish; } CFArrayAppendValue(toolArgs->argURLs, scratchURL); scratchString = CFURLCopyPathExtension(scratchURL); if (scratchString && CFEqual(scratchString, CFSTR("kext"))) { CFArrayAppendValue(toolArgs->namedKextURLs, scratchURL); } else { CFArrayAppendValue(toolArgs->repositoryURLs, scratchURL); } } result = EX_OK; finish: SAFE_RELEASE(scratchString); SAFE_RELEASE(scratchNumber); SAFE_RELEASE(scratchURL); if (result == EX_USAGE) { usage(kUsageLevelBrief); } return result; }