Mercurial > foo_out_sdl
diff foosdk/sdk/foobar2000/SDK/filesystem_helper.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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/foosdk/sdk/foobar2000/SDK/filesystem_helper.cpp Mon Jan 05 02:15:46 2026 -0500 @@ -0,0 +1,298 @@ +#include "foobar2000-sdk-pch.h" +#include "filesystem_helper.h" + +void stream_writer_chunk::write(const void * p_buffer,t_size p_bytes,abort_callback & p_abort) { + t_size remaining = p_bytes, written = 0; + while(remaining > 0) { + t_size delta = sizeof(m_buffer) - m_buffer_state; + if (delta > remaining) delta = remaining; + memcpy(m_buffer,(const t_uint8*)p_buffer + written,delta); + written += delta; + remaining -= delta; + + if (m_buffer_state == sizeof(m_buffer)) { + m_writer->write_lendian_t((t_uint8)m_buffer_state,p_abort); + m_writer->write_object(m_buffer,m_buffer_state,p_abort); + m_buffer_state = 0; + } + } +} + +void stream_writer_chunk::flush(abort_callback & p_abort) +{ + m_writer->write_lendian_t((t_uint8)m_buffer_state,p_abort); + if (m_buffer_state > 0) { + m_writer->write_object(m_buffer,m_buffer_state,p_abort); + m_buffer_state = 0; + } +} + +/* + stream_writer * m_writer; + unsigned m_buffer_state; + unsigned char m_buffer[255]; +*/ + +t_size stream_reader_chunk::read(void * p_buffer,t_size p_bytes,abort_callback & p_abort) +{ + t_size todo = p_bytes, done = 0; + while(todo > 0) { + if (m_buffer_size == m_buffer_state) { + if (m_eof) break; + t_uint8 temp; + m_reader->read_lendian_t(temp,p_abort); + m_buffer_size = temp; + if (temp != sizeof(m_buffer)) m_eof = true; + m_buffer_state = 0; + if (m_buffer_size>0) { + m_reader->read_object(m_buffer,m_buffer_size,p_abort); + } + } + + + t_size delta = m_buffer_size - m_buffer_state; + if (delta > todo) delta = todo; + if (delta > 0) { + memcpy((unsigned char*)p_buffer + done,m_buffer + m_buffer_state,delta); + todo -= delta; + done += delta; + m_buffer_state += delta; + } + } + return done; +} + +void stream_reader_chunk::flush(abort_callback & p_abort) { + while(!m_eof) { + p_abort.check_e(); + t_uint8 temp; + m_reader->read_lendian_t(temp,p_abort); + m_buffer_size = temp; + if (temp != sizeof(m_buffer)) m_eof = true; + m_buffer_state = 0; + if (m_buffer_size>0) { + m_reader->skip_object(m_buffer_size,p_abort); + } + } +} + +/* + stream_reader * m_reader; + unsigned m_buffer_state, m_buffer_size; + bool m_eof; + unsigned char m_buffer[255]; +*/ + +void stream_reader_chunk::g_skip(stream_reader * p_stream,abort_callback & p_abort) { + stream_reader_chunk(p_stream).flush(p_abort); +} + + + +static void fileSanitySeek(file::ptr f, pfc::array_t<uint8_t> const & content, size_t offset, abort_callback & aborter) { + const size_t readAmount = 64 * 1024; + pfc::array_staticsize_t<uint8_t> buf; buf.set_size_discard(readAmount); + f->seek(offset, aborter); + t_filesize positionGot = f->get_position(aborter); + if (positionGot != offset) { + FB2K_console_formatter() << "File sanity: at " << offset << " reported position became " << positionGot; + throw std::runtime_error("Seek test failure"); + } + size_t did = f->read(buf.get_ptr(), readAmount, aborter); + size_t expected = pfc::min_t<size_t>(readAmount, content.get_size() - offset); + if (expected != did) { + FB2K_console_formatter() << "File sanity: at " << offset << " bytes, expected read size of " << expected << ", got " << did; + if (did > expected) FB2K_console_formatter() << "Read past EOF"; + else FB2K_console_formatter() << "Premature EOF"; + throw std::runtime_error("Seek test failure"); + } + if (memcmp(buf.get_ptr(), content.get_ptr() + offset, did) != 0) { + FB2K_console_formatter() << "File sanity: data mismatch at " << offset << " - " << (offset + did) << " bytes"; + throw std::runtime_error("Seek test failure"); + } + positionGot = f->get_position(aborter); + if (positionGot != offset + did) { + FB2K_console_formatter() << "File sanity: at " << offset << "+" << did << "=" << (offset + did) << " reported position became " << positionGot; + throw std::runtime_error("Seek test failure"); + } +} + +bool fb2kFileSelfTest(file::ptr f, abort_callback & aborter) { + try { + pfc::array_t<uint8_t> fileContent; + f->reopen(aborter); + f->read_till_eof(fileContent, aborter); + + { + t_filesize sizeClaimed = f->get_size(aborter); + if (sizeClaimed == filesize_invalid) { + FB2K_console_formatter() << "File sanity: file reports unknown size, actual size read is " << fileContent.get_size(); + } + else { + if (sizeClaimed != fileContent.get_size()) { + FB2K_console_formatter() << "File sanity: file reports size of " << sizeClaimed << ", actual size read is " << fileContent.get_size(); + throw std::runtime_error("File size mismatch"); + } + else { + FB2K_console_formatter() << "File sanity: file size check OK: " << sizeClaimed; + } + } + } + + { + FB2K_console_formatter() << "File sanity: testing N-first-bytes reads..."; + const size_t sizeUpTo = pfc::min_t<size_t>(fileContent.get_size(), 1024 * 1024); + pfc::array_staticsize_t<uint8_t> buf1; + buf1.set_size_discard(sizeUpTo); + + for (size_t w = 1; w <= sizeUpTo; w <<= 1) { + f->reopen(aborter); + size_t did = f->read(buf1.get_ptr(), w, aborter); + if (did != w) { + FB2K_console_formatter() << "File sanity: premature EOF reading first " << w << " bytes, got " << did; + throw std::runtime_error("Premature EOF"); + } + if (memcmp(fileContent.get_ptr(), buf1.get_ptr(), did) != 0) { + FB2K_console_formatter() << "File sanity: file content mismatch reading first " << w << " bytes"; + throw std::runtime_error("File content mismatch"); + } + } + } + if (f->can_seek()) { + FB2K_console_formatter() << "File sanity: testing random access..."; + + { + size_t sizeUpTo = pfc::min_t<size_t>(fileContent.get_size(), 1024 * 1024); + for (size_t w = 1; w < sizeUpTo; w <<= 1) { + fileSanitySeek(f, fileContent, w, aborter); + } + fileSanitySeek(f, fileContent, fileContent.get_size(), aborter); + for (size_t w = 1; w < sizeUpTo; w <<= 1) { + fileSanitySeek(f, fileContent, fileContent.get_size() - w, aborter); + } + fileSanitySeek(f, fileContent, fileContent.get_size() / 2, aborter); + } + } + FB2K_console_formatter() << "File sanity test: all OK"; + return true; + } + catch (std::exception const & e) { + FB2K_console_formatter() << "File sanity test failure: " << e.what(); + return false; + } +} + + +namespace foobar2000_io { + void retryFileDelete(double timeout, abort_callback & a, std::function<void()> f) { + FB2K_RETRY_ON_EXCEPTION3(f(), a, timeout, exception_io_sharing_violation, exception_io_denied, exception_io_directory_not_empty); + } + void retryFileMove(double timeout, abort_callback & a, std::function<void() > f) { + FB2K_RETRY_FILE_MOVE( f(), a, timeout ); + } + void retryOnSharingViolation(double timeout, abort_callback & a, std::function<void() > f) { + FB2K_RETRY_ON_SHARING_VIOLATION(f(), a, timeout); + } + void retryOnSharingViolation(std::function<void() > f, double timeout, abort_callback & a) { + FB2K_RETRY_ON_SHARING_VIOLATION( f(), a, timeout ); + } + void listDirectory( const char * path, abort_callback & aborter, listDirectoryFunc_t func) { + listDirectoryCallbackImpl cb; cb.m_func = func; + filesystem::g_list_directory(path, cb, aborter); + } + + pfc::string8 stripParentFolders( const char * inPath ) { + PFC_ASSERT( strstr(inPath, "://" ) == nullptr || matchProtocol( inPath, "file" ) ); + + size_t prefixLen = pfc::string_find_first(inPath, "://"); + if ( prefixLen != pfc_infinite ) prefixLen += 3; + else prefixLen = 0; + + pfc::chain_list_v2_t<pfc::string_part_ref> segments; + + const char separator = + #ifdef _WIN32 + '\\' + #else + '/' + #endif + ; + const char strSeparator[] = {separator, 0}; + + pfc::splitStringByChar(segments, inPath + prefixLen, separator ); + for ( auto i = segments.first(); i.is_valid(); ) { + auto n = i; ++n; + if ( i->equals( "." ) ) { + segments.remove_single( i ); + } else if ( i->equals( ".." ) ) { + auto p = i; --p; + if ( p.is_valid() ) segments.remove_single( p ); + segments.remove_single( i ); + } + i = n; + } + pfc::string8 ret; + if ( prefixLen > 0 ) ret.add_string( inPath, prefixLen ); + bool bFirst = true; + for ( auto i = segments.first(); i.is_valid(); ++ i ) { + if (!bFirst) ret << strSeparator; + ret << *i; + bFirst = false; + } + return ret; + } +#ifdef _WIN32 + pfc::string8 winGetVolumePath(const char * fb2kPath) { + PFC_ASSERT(matchProtocol(fb2kPath, "file")); + pfc::string8 native; + if (!filesystem::g_get_native_path(fb2kPath, native)) throw pfc::exception_invalid_params(); + + TCHAR outBuffer[MAX_PATH+1] = {}; + WIN32_IO_OP( GetVolumePathName( pfc::stringcvt::string_os_from_utf8( native ), outBuffer, MAX_PATH ) ); + return pfc::stringcvt::string_utf8_from_os( outBuffer ).get_ptr(); + } + + DWORD winVolumeFlags( const char * fb2kPath ) { + PFC_ASSERT(matchProtocol(fb2kPath, "file")); + pfc::string8 native; + if (!filesystem::g_get_native_path(fb2kPath, native)) throw pfc::exception_invalid_params(); + + TCHAR outBuffer[MAX_PATH + 1] = {}; + WIN32_IO_OP(GetVolumePathName(pfc::stringcvt::string_os_from_utf8(native), outBuffer, MAX_PATH)); + + DWORD flags = 0; + WIN32_IO_OP(GetVolumeInformation(outBuffer, nullptr, 0, nullptr, nullptr, &flags, nullptr, 0)); + return flags; + } +#endif +} + +pfc::string8 file_path_canonical(const char* src) { + pfc::string8 ret; + filesystem::g_get_canonical_path(src, ret); + return ret; +} + +pfc::string8 file_path_display(const char* src) { + pfc::string8 ret; + filesystem::g_get_display_path(src, ret); + return ret; +} + +pfc::string8 fb2k::filename_ext( const char * path, filesystem::ptr & fs) { + if ( fs.is_empty() || ! fs->is_our_path( path ) ) { + fs = filesystem::tryGet( path ); + } + if ( fs.is_valid() ) return fs->extract_filename_ext( path ); + // UGLY FALLBACK + return pfc::string_filename_ext( path ); + +} +pfc::string8 fb2k::filename_ext( const char * path ) { + filesystem::ptr no_reuse; + return filename_ext( path, no_reuse ); +} + +pfc::string8 fb2k::filename( const char * path ) { + return pfc::remove_ext_v2( filename_ext( path ) ); +}
