Mercurial > foo_out_sdl
comparison foosdk/sdk/foobar2000/SDK/input.cpp @ 1:20d02a178406 default tip
*: check in everything else
yay
| author | Paper <paper@tflc.us> |
|---|---|
| date | Mon, 05 Jan 2026 02:15:46 -0500 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 0:e9bb126753e7 | 1:20d02a178406 |
|---|---|
| 1 #include "foobar2000-sdk-pch.h" | |
| 2 #include <exception> | |
| 3 #include "input.h" | |
| 4 #include "input_impl.h" | |
| 5 #include "album_art.h" | |
| 6 #include "file_info_impl.h" | |
| 7 | |
| 8 service_ptr input_entry::open(const GUID & whatFor, file::ptr hint, const char * path, event_logger::ptr logger, abort_callback & aborter) { | |
| 9 | |
| 10 #ifdef FOOBAR2000_DESKTOP | |
| 11 if ( whatFor == input_stream_info_reader::class_guid ) { | |
| 12 input_entry_v2::ptr v2; | |
| 13 if ( v2 &= this ) { | |
| 14 GUID g = v2->get_guid(); | |
| 15 for (auto p : input_stream_info_reader_entry::enumerate()) { | |
| 16 if (p->get_guid() == g) { | |
| 17 return p->open(path, hint, aborter); | |
| 18 } | |
| 19 } | |
| 20 } | |
| 21 throw exception_io_unsupported_format(); | |
| 22 } | |
| 23 #endif | |
| 24 if ( whatFor == album_art_extractor_instance::class_guid ) { | |
| 25 input_entry_v2::ptr v2; | |
| 26 if (v2 &= this) { | |
| 27 GUID g = v2->get_guid(); | |
| 28 for (auto p : album_art_extractor::enumerate()) { | |
| 29 if (p->get_guid() == g) { | |
| 30 return p->open(hint, path, aborter); | |
| 31 } | |
| 32 } | |
| 33 } | |
| 34 throw exception_io_unsupported_format(); | |
| 35 } | |
| 36 if ( whatFor == album_art_editor_instance::class_guid ) { | |
| 37 input_entry_v2::ptr v2; | |
| 38 if (v2 &= this) { | |
| 39 GUID g = v2->get_guid(); | |
| 40 for (auto p : album_art_editor::enumerate()) { | |
| 41 if (p->get_guid() == g) { | |
| 42 return p->open(hint, path, aborter); | |
| 43 } | |
| 44 } | |
| 45 } | |
| 46 throw exception_io_unsupported_format(); | |
| 47 } | |
| 48 | |
| 49 input_entry_v3::ptr v3; | |
| 50 | |
| 51 if (v3 &= this) { | |
| 52 return v3->open_v3( whatFor, hint, path, logger, aborter ); | |
| 53 } else { | |
| 54 if (whatFor == input_decoder::class_guid) { | |
| 55 input_decoder::ptr obj; | |
| 56 open(obj, hint, path, aborter); | |
| 57 if ( logger.is_valid() ) { | |
| 58 input_decoder_v2::ptr v2; | |
| 59 if (v2 &= obj) v2->set_logger(logger); | |
| 60 } | |
| 61 return obj; | |
| 62 } | |
| 63 if (whatFor == input_info_reader::class_guid) { | |
| 64 input_info_reader::ptr obj; | |
| 65 open(obj, hint, path, aborter); | |
| 66 return obj; | |
| 67 } | |
| 68 if (whatFor == input_info_writer::class_guid) { | |
| 69 input_info_writer::ptr obj; | |
| 70 open(obj, hint, path, aborter); | |
| 71 return obj; | |
| 72 } | |
| 73 } | |
| 74 | |
| 75 throw pfc::exception_not_implemented(); | |
| 76 } | |
| 77 | |
| 78 bool input_entry::g_find_service_by_path(service_ptr_t<input_entry> & p_out,const char * p_path) | |
| 79 { | |
| 80 auto ext = pfc::string_extension(p_path); | |
| 81 return g_find_service_by_path(p_out, p_path, ext ); | |
| 82 } | |
| 83 | |
| 84 bool input_entry::g_find_service_by_path(service_ptr_t<input_entry> & p_out,const char * p_path, const char * p_ext) | |
| 85 { | |
| 86 for (auto ptr : enumerate()) { | |
| 87 if (ptr->is_our_path(p_path,p_ext)) { | |
| 88 p_out = ptr; | |
| 89 return true; | |
| 90 } | |
| 91 } | |
| 92 return false; | |
| 93 } | |
| 94 | |
| 95 bool input_entry::g_find_service_by_content_type(service_ptr_t<input_entry> & p_out,const char * p_content_type) | |
| 96 { | |
| 97 for (auto ptr : enumerate()) { | |
| 98 if (ptr->is_our_content_type(p_content_type)) { | |
| 99 p_out = ptr; | |
| 100 return true; | |
| 101 } | |
| 102 } | |
| 103 return false; | |
| 104 } | |
| 105 | |
| 106 | |
| 107 #if 0 | |
| 108 static void prepare_for_open(service_ptr_t<input_entry> & p_service,service_ptr_t<file> & p_file,const char * p_path,filesystem::t_open_mode p_open_mode,abort_callback & p_abort,bool p_from_redirect) | |
| 109 { | |
| 110 if (p_file.is_empty()) | |
| 111 { | |
| 112 service_ptr_t<filesystem> fs; | |
| 113 if (filesystem::g_get_interface(fs,p_path)) { | |
| 114 if (fs->supports_content_types()) { | |
| 115 fs->open(p_file,p_path,p_open_mode,p_abort); | |
| 116 } | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 if (p_file.is_valid()) | |
| 121 { | |
| 122 pfc::string8 content_type; | |
| 123 if (p_file->get_content_type(content_type)) | |
| 124 { | |
| 125 if (input_entry::g_find_service_by_content_type(p_service,content_type)) | |
| 126 return; | |
| 127 } | |
| 128 } | |
| 129 | |
| 130 if (input_entry::g_find_service_by_path(p_service,p_path)) | |
| 131 { | |
| 132 if (p_from_redirect && p_service->is_redirect()) throw exception_io_unsupported_format(); | |
| 133 return; | |
| 134 } | |
| 135 | |
| 136 throw exception_io_unsupported_format(); | |
| 137 } | |
| 138 #endif | |
| 139 | |
| 140 bool input_entry::g_find_inputs_by_content_type(pfc::list_base_t<service_ptr_t<input_entry> > & p_out, const char * p_content_type, bool p_from_redirect) { | |
| 141 auto filter = [=] (input_entry::ptr p) { | |
| 142 return !(p_from_redirect && p->is_redirect()); | |
| 143 }; | |
| 144 return g_find_inputs_by_content_type_ex(p_out, p_content_type, filter ); | |
| 145 } | |
| 146 | |
| 147 bool input_entry::g_find_inputs_by_path(pfc::list_base_t<service_ptr_t<input_entry> > & p_out, const char * p_path, bool p_from_redirect) { | |
| 148 auto filter = [=] (input_entry::ptr p) { | |
| 149 return !(p_from_redirect && p->is_redirect()); | |
| 150 }; | |
| 151 return g_find_inputs_by_path_ex(p_out, p_path, filter); | |
| 152 } | |
| 153 | |
| 154 bool input_entry::g_find_inputs_by_content_type_ex(pfc::list_base_t<service_ptr_t<input_entry> > & p_out, const char * p_content_type, input_filter_t filter ) { | |
| 155 bool ret = false; | |
| 156 for (auto ptr : enumerate()) { | |
| 157 if (filter(ptr)) { | |
| 158 if (ptr->is_our_content_type(p_content_type)) { p_out.add_item(ptr); ret = true; } | |
| 159 } | |
| 160 } | |
| 161 return ret; | |
| 162 } | |
| 163 | |
| 164 bool input_entry::g_find_inputs_by_path_ex(pfc::list_base_t<service_ptr_t<input_entry> > & p_out, const char * p_path, input_filter_t filter ) { | |
| 165 auto extension = pfc::string_extension(p_path); | |
| 166 bool ret = false; | |
| 167 for( auto ptr : enumerate()) { | |
| 168 GUID guid = pfc::guid_null; | |
| 169 input_entry_v3::ptr ex; | |
| 170 if ( ex &= ptr ) guid = ex->get_guid(); | |
| 171 if ( filter(ptr) ) { | |
| 172 if (ptr->is_our_path(p_path, extension)) { p_out.add_item(ptr); ret = true; } | |
| 173 } | |
| 174 } | |
| 175 return ret; | |
| 176 } | |
| 177 | |
| 178 static GUID input_get_guid( input_entry::ptr e ) { | |
| 179 #ifdef FOOBAR2000_DESKTOP | |
| 180 input_entry_v2::ptr p; | |
| 181 if ( p &= e ) return p->get_guid(); | |
| 182 #endif | |
| 183 return pfc::guid_null; | |
| 184 } | |
| 185 | |
| 186 service_ptr input_entry::g_open_from_list(input_entry_list_t const & p_list, const GUID & whatFor, service_ptr_t<file> p_filehint, const char * p_path, event_logger::ptr logger, abort_callback & p_abort, GUID * outGUID) { | |
| 187 const t_size count = p_list.get_count(); | |
| 188 if ( count == 0 ) { | |
| 189 // sanity | |
| 190 throw exception_io_unsupported_format(); | |
| 191 } else if (count == 1) { | |
| 192 auto ret = p_list[0]->open(whatFor, p_filehint, p_path, logger, p_abort); | |
| 193 if ( outGUID != nullptr ) * outGUID = input_get_guid( p_list[0] ); | |
| 194 return ret; | |
| 195 } else { | |
| 196 std::exception_ptr errData, errUnsupported; | |
| 197 for (t_size n = 0; n < count; n++) { | |
| 198 try { | |
| 199 auto ret = p_list[n]->open(whatFor, p_filehint, p_path, logger, p_abort); | |
| 200 if (outGUID != nullptr) * outGUID = input_get_guid(p_list[n]); | |
| 201 return ret; | |
| 202 } catch (exception_io_no_handler_for_path const &) { | |
| 203 //do nothing, skip over | |
| 204 } catch(exception_io_unsupported_format const &) { | |
| 205 if (!errUnsupported) errUnsupported = std::current_exception(); | |
| 206 } catch (exception_io_data const &) { | |
| 207 if (!errData) errData = std::current_exception(); | |
| 208 } | |
| 209 } | |
| 210 if (errData) std::rethrow_exception(errData); | |
| 211 if (errUnsupported) std::rethrow_exception(errUnsupported); | |
| 212 throw exception_io_unsupported_format(); | |
| 213 } | |
| 214 } | |
| 215 | |
| 216 #ifdef FOOBAR2000_DESKTOP | |
| 217 service_ptr input_manager::open_v2(const GUID & whatFor, file::ptr hint, const char * path, bool fromRedirect, event_logger::ptr logger, abort_callback & aborter, GUID * outUsedEntry) { | |
| 218 // We're wrapping open_v2() on top of old open(). | |
| 219 // Assert on GUIDs that old open() is known to recognize. | |
| 220 PFC_ASSERT(whatFor == input_decoder::class_guid || whatFor == input_info_reader::class_guid || whatFor == input_info_writer::class_guid || whatFor == input_stream_selector::class_guid); | |
| 221 | |
| 222 { | |
| 223 input_manager_v2::ptr v2; | |
| 224 if ( v2 &= this ) { | |
| 225 return v2->open_v2( whatFor, hint, path, fromRedirect, logger, aborter, outUsedEntry ); | |
| 226 } | |
| 227 } | |
| 228 | |
| 229 auto ret = open( whatFor, hint, path, fromRedirect, aborter, outUsedEntry ); | |
| 230 | |
| 231 #ifdef FB2K_HAVE_EVENT_LOGGER | |
| 232 if ( logger.is_valid() ) { | |
| 233 input_decoder_v2::ptr dec; | |
| 234 if (dec &= ret) { | |
| 235 dec->set_logger(logger); | |
| 236 } | |
| 237 } | |
| 238 #endif | |
| 239 return ret; | |
| 240 } | |
| 241 #endif | |
| 242 | |
| 243 service_ptr input_entry::g_open(const GUID & whatFor, file::ptr p_filehint, const char * p_path, event_logger::ptr logger, abort_callback & p_abort, bool p_from_redirect) { | |
| 244 | |
| 245 #ifdef FOOBAR2000_DESKTOP | |
| 246 return input_manager_v2::get()->open_v2(whatFor, p_filehint, p_path, p_from_redirect, logger, p_abort); | |
| 247 #else // FOOBAR2000_DESKTOP or not | |
| 248 const bool needWriteAcecss = !!(whatFor == input_info_writer::class_guid); | |
| 249 | |
| 250 service_ptr_t<file> l_file = p_filehint; | |
| 251 if (l_file.is_empty()) { | |
| 252 service_ptr_t<filesystem> fs; | |
| 253 if (filesystem::g_get_interface(fs, p_path)) { | |
| 254 if (fs->supports_content_types()) { | |
| 255 fs->open(l_file, p_path, needWriteAcecss ? filesystem::open_mode_write_existing : filesystem::open_mode_read, p_abort); | |
| 256 } | |
| 257 } | |
| 258 } | |
| 259 | |
| 260 if (l_file.is_valid()) { | |
| 261 pfc::string8 content_type; | |
| 262 if (l_file->get_content_type(content_type)) { | |
| 263 pfc::list_t< input_entry::ptr > list; | |
| 264 #if PFC_DEBUG | |
| 265 FB2K_DebugLog() << "attempting input open by content type: " << content_type; | |
| 266 #endif | |
| 267 if (g_find_inputs_by_content_type(list, content_type, p_from_redirect)) { | |
| 268 try { | |
| 269 return g_open_from_list(list, whatFor, l_file, p_path, logger, p_abort); | |
| 270 } catch (exception_io_unsupported_format const &) { | |
| 271 #if PFC_DEBUG | |
| 272 FB2K_DebugLog() << "Failed to open by content type, using fallback"; | |
| 273 #endif | |
| 274 } | |
| 275 } | |
| 276 } | |
| 277 } | |
| 278 | |
| 279 #if PFC_DEBUG | |
| 280 FB2K_DebugLog() << "attempting input open by path: " << p_path; | |
| 281 #endif | |
| 282 { | |
| 283 pfc::list_t< input_entry::ptr > list; | |
| 284 if (g_find_inputs_by_path(list, p_path, p_from_redirect)) { | |
| 285 return g_open_from_list(list, whatFor, l_file, p_path, logger, p_abort); | |
| 286 } | |
| 287 } | |
| 288 | |
| 289 throw exception_io_unsupported_format(); | |
| 290 #endif // not FOOBAR2000_DESKTOP | |
| 291 } | |
| 292 | |
| 293 void input_entry::g_open_for_decoding(service_ptr_t<input_decoder> & p_instance,service_ptr_t<file> p_filehint,const char * p_path,abort_callback & p_abort,bool p_from_redirect) { | |
| 294 TRACK_CALL_TEXT("input_entry::g_open_for_decoding"); | |
| 295 p_instance ^= g_open(input_decoder::class_guid, p_filehint, p_path, nullptr, p_abort, p_from_redirect); | |
| 296 } | |
| 297 | |
| 298 void input_entry::g_open_for_info_read(service_ptr_t<input_info_reader> & p_instance,service_ptr_t<file> p_filehint,const char * p_path,abort_callback & p_abort,bool p_from_redirect) { | |
| 299 TRACK_CALL_TEXT("input_entry::g_open_for_info_read"); | |
| 300 p_instance ^= g_open(input_info_reader::class_guid, p_filehint, p_path, nullptr, p_abort, p_from_redirect); | |
| 301 } | |
| 302 | |
| 303 void input_entry::g_open_for_info_write(service_ptr_t<input_info_writer> & p_instance,service_ptr_t<file> p_filehint,const char * p_path,abort_callback & p_abort,bool p_from_redirect) { | |
| 304 TRACK_CALL_TEXT("input_entry::g_open_for_info_write"); | |
| 305 p_instance ^= g_open(input_info_writer::class_guid, p_filehint, p_path, nullptr, p_abort, p_from_redirect); | |
| 306 } | |
| 307 | |
| 308 void input_entry::g_open_for_info_write_timeout(service_ptr_t<input_info_writer> & p_instance,service_ptr_t<file> p_filehint,const char * p_path,abort_callback & p_abort,double p_timeout,bool p_from_redirect) { | |
| 309 pfc::lores_timer timer; | |
| 310 timer.start(); | |
| 311 for(;;) { | |
| 312 try { | |
| 313 g_open_for_info_write(p_instance,p_filehint,p_path,p_abort,p_from_redirect); | |
| 314 break; | |
| 315 } catch(exception_io_sharing_violation const &) { | |
| 316 if (timer.query() > p_timeout) throw; | |
| 317 p_abort.sleep(0.01); | |
| 318 } | |
| 319 } | |
| 320 } | |
| 321 | |
| 322 bool input_entry::g_is_supported_path(const char * p_path) | |
| 323 { | |
| 324 auto ext = pfc::string_extension (p_path); | |
| 325 for( auto ptr : enumerate() ) { | |
| 326 if (ptr->is_our_path(p_path,ext)) return true; | |
| 327 } | |
| 328 return false; | |
| 329 } | |
| 330 | |
| 331 | |
| 332 | |
| 333 void input_open_file_helper(service_ptr_t<file> & p_file,const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort) | |
| 334 { | |
| 335 if (p_file.is_empty()) { | |
| 336 switch(p_reason) { | |
| 337 default: | |
| 338 uBugCheck(); | |
| 339 case input_open_info_read: | |
| 340 case input_open_decode: | |
| 341 filesystem::g_open(p_file,p_path,filesystem::open_mode_read,p_abort); | |
| 342 break; | |
| 343 case input_open_info_write: | |
| 344 filesystem::g_open(p_file,p_path,filesystem::open_mode_write_existing,p_abort); | |
| 345 break; | |
| 346 } | |
| 347 } else { | |
| 348 p_file->reopen(p_abort); | |
| 349 } | |
| 350 } | |
| 351 | |
| 352 uint32_t input_entry::g_flags_for_path( const char * path, uint32_t mask ) { | |
| 353 #if defined(FOOBAR2000_DESKTOP) && FOOBAR2000_TARGET_VERSION >= 80 | |
| 354 return input_manager_v3::get()->flags_for_path(path, mask); | |
| 355 #else | |
| 356 #ifdef FOOBAR2000_DESKTOP | |
| 357 input_manager_v3::ptr api; | |
| 358 if ( input_manager_v3::tryGet(api) ) { | |
| 359 return api->flags_for_path(path, mask); | |
| 360 } | |
| 361 #endif | |
| 362 uint32_t ret = 0; | |
| 363 service_enum_t<input_entry> e; input_entry::ptr p; | |
| 364 auto ext = pfc::string_extension(path); | |
| 365 while(e.next(p)) { | |
| 366 uint32_t f = p->get_flags() & mask; | |
| 367 if ( f != 0 && p->is_our_path( path, ext ) ) ret |= f;; | |
| 368 } | |
| 369 return ret; | |
| 370 #endif | |
| 371 } | |
| 372 uint32_t input_entry::g_flags_for_content_type( const char * ct, uint32_t mask ) { | |
| 373 #if defined(FOOBAR2000_DESKTOP) && FOOBAR2000_TARGET_VERSION >= 80 | |
| 374 return input_manager_v3::get()->flags_for_content_type(ct, mask); | |
| 375 #else | |
| 376 #ifdef FOOBAR2000_DESKTOP | |
| 377 input_manager_v3::ptr api; | |
| 378 if ( input_manager_v3::tryGet(api) ) { | |
| 379 return api->flags_for_content_type( ct, mask ); | |
| 380 } | |
| 381 #endif | |
| 382 uint32_t ret = 0; | |
| 383 service_enum_t<input_entry> e; input_entry::ptr p; | |
| 384 while(e.next(p)) { | |
| 385 uint32_t f = p->get_flags() & mask; | |
| 386 if ( f != 0 && p->is_our_content_type(ct) ) ret |= f; | |
| 387 } | |
| 388 return ret; | |
| 389 #endif | |
| 390 } | |
| 391 | |
| 392 bool input_entry::g_are_parallel_reads_slow(const char * path) { | |
| 393 return g_flags_for_path(path, flag_parallel_reads_slow) != 0; | |
| 394 } | |
| 395 | |
| 396 void input_entry_v3::open_for_decoding(service_ptr_t<input_decoder> & p_instance, service_ptr_t<file> p_filehint, const char * p_path, abort_callback & p_abort) { | |
| 397 p_instance ^= open_v3( input_decoder::class_guid, p_filehint, p_path, nullptr, p_abort ); | |
| 398 } | |
| 399 void input_entry_v3::open_for_info_read(service_ptr_t<input_info_reader> & p_instance, service_ptr_t<file> p_filehint, const char * p_path, abort_callback & p_abort) { | |
| 400 p_instance ^= open_v3(input_info_reader::class_guid, p_filehint, p_path, nullptr, p_abort); | |
| 401 } | |
| 402 void input_entry_v3::open_for_info_write(service_ptr_t<input_info_writer> & p_instance, service_ptr_t<file> p_filehint, const char * p_path, abort_callback & p_abort) { | |
| 403 p_instance ^= open_v3(input_info_writer::class_guid, p_filehint, p_path, nullptr, p_abort); | |
| 404 } | |
| 405 | |
| 406 void input_info_writer::remove_tags_fallback(abort_callback & abort) { | |
| 407 uint32_t total = this->get_subsong_count(); | |
| 408 file_info_impl blank; | |
| 409 for( uint32_t walk = 0; walk < total; ++ walk ) { | |
| 410 this->set_info( this->get_subsong(walk), blank, abort ); | |
| 411 } | |
| 412 this->commit( abort ); | |
| 413 } | |
| 414 | |
| 415 t_filestats input_info_reader_v2::get_file_stats(abort_callback& a) { | |
| 416 return this->get_stats2(stats2_size | stats2_timestamp, a).to_legacy(); | |
| 417 } | |
| 418 | |
| 419 t_filestats2 input_info_reader::get_stats2_(const char* fallbackPath, uint32_t f, abort_callback& a) { | |
| 420 t_filestats2 ret; | |
| 421 input_info_reader_v2::ptr v2; | |
| 422 if (v2 &= this) { | |
| 423 ret = v2->get_stats2(f, a); | |
| 424 } else if ((f & ~stats2_legacy) == 0) { | |
| 425 t_filestats subset = this->get_file_stats(a); | |
| 426 ret.m_size = subset.m_size; | |
| 427 ret.m_timestamp = subset.m_timestamp; | |
| 428 } else { | |
| 429 try { | |
| 430 auto fs = filesystem::tryGet(fallbackPath); | |
| 431 if (fs.is_valid()) ret = fs->get_stats2_(fallbackPath, f, a); | |
| 432 } catch (exception_io const &) {} | |
| 433 } | |
| 434 return ret; | |
| 435 } | |
| 436 | |
| 437 GUID input_entry::get_guid_() { | |
| 438 auto ret = pfc::guid_null; | |
| 439 input_entry_v2::ptr v2; | |
| 440 if ( v2 &= this ) ret = v2->get_guid(); | |
| 441 return ret; | |
| 442 } | |
| 443 | |
| 444 const char* input_entry::get_name_() { | |
| 445 const char * ret = "<legacy object>"; | |
| 446 input_entry_v2::ptr v2; | |
| 447 if ( v2 &= this ) ret = v2->get_name(); | |
| 448 return ret; | |
| 449 } | |
| 450 | |
| 451 input_entry::ptr input_entry::g_find_by_guid(const GUID& guid) { | |
| 452 for (auto ptr : enumerate()) { | |
| 453 input_entry_v2::ptr v2; | |
| 454 if (v2 &= ptr) { | |
| 455 if ( guid == v2->get_guid() ) return v2; | |
| 456 } | |
| 457 } | |
| 458 return nullptr; | |
| 459 } |
