Mercurial > foo_out_sdl
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 0:e9bb126753e7 | 1:20d02a178406 |
|---|---|
| 1 #include "foobar2000-sdk-pch.h" | |
| 2 #include "filesystem_helper.h" | |
| 3 | |
| 4 void stream_writer_chunk::write(const void * p_buffer,t_size p_bytes,abort_callback & p_abort) { | |
| 5 t_size remaining = p_bytes, written = 0; | |
| 6 while(remaining > 0) { | |
| 7 t_size delta = sizeof(m_buffer) - m_buffer_state; | |
| 8 if (delta > remaining) delta = remaining; | |
| 9 memcpy(m_buffer,(const t_uint8*)p_buffer + written,delta); | |
| 10 written += delta; | |
| 11 remaining -= delta; | |
| 12 | |
| 13 if (m_buffer_state == sizeof(m_buffer)) { | |
| 14 m_writer->write_lendian_t((t_uint8)m_buffer_state,p_abort); | |
| 15 m_writer->write_object(m_buffer,m_buffer_state,p_abort); | |
| 16 m_buffer_state = 0; | |
| 17 } | |
| 18 } | |
| 19 } | |
| 20 | |
| 21 void stream_writer_chunk::flush(abort_callback & p_abort) | |
| 22 { | |
| 23 m_writer->write_lendian_t((t_uint8)m_buffer_state,p_abort); | |
| 24 if (m_buffer_state > 0) { | |
| 25 m_writer->write_object(m_buffer,m_buffer_state,p_abort); | |
| 26 m_buffer_state = 0; | |
| 27 } | |
| 28 } | |
| 29 | |
| 30 /* | |
| 31 stream_writer * m_writer; | |
| 32 unsigned m_buffer_state; | |
| 33 unsigned char m_buffer[255]; | |
| 34 */ | |
| 35 | |
| 36 t_size stream_reader_chunk::read(void * p_buffer,t_size p_bytes,abort_callback & p_abort) | |
| 37 { | |
| 38 t_size todo = p_bytes, done = 0; | |
| 39 while(todo > 0) { | |
| 40 if (m_buffer_size == m_buffer_state) { | |
| 41 if (m_eof) break; | |
| 42 t_uint8 temp; | |
| 43 m_reader->read_lendian_t(temp,p_abort); | |
| 44 m_buffer_size = temp; | |
| 45 if (temp != sizeof(m_buffer)) m_eof = true; | |
| 46 m_buffer_state = 0; | |
| 47 if (m_buffer_size>0) { | |
| 48 m_reader->read_object(m_buffer,m_buffer_size,p_abort); | |
| 49 } | |
| 50 } | |
| 51 | |
| 52 | |
| 53 t_size delta = m_buffer_size - m_buffer_state; | |
| 54 if (delta > todo) delta = todo; | |
| 55 if (delta > 0) { | |
| 56 memcpy((unsigned char*)p_buffer + done,m_buffer + m_buffer_state,delta); | |
| 57 todo -= delta; | |
| 58 done += delta; | |
| 59 m_buffer_state += delta; | |
| 60 } | |
| 61 } | |
| 62 return done; | |
| 63 } | |
| 64 | |
| 65 void stream_reader_chunk::flush(abort_callback & p_abort) { | |
| 66 while(!m_eof) { | |
| 67 p_abort.check_e(); | |
| 68 t_uint8 temp; | |
| 69 m_reader->read_lendian_t(temp,p_abort); | |
| 70 m_buffer_size = temp; | |
| 71 if (temp != sizeof(m_buffer)) m_eof = true; | |
| 72 m_buffer_state = 0; | |
| 73 if (m_buffer_size>0) { | |
| 74 m_reader->skip_object(m_buffer_size,p_abort); | |
| 75 } | |
| 76 } | |
| 77 } | |
| 78 | |
| 79 /* | |
| 80 stream_reader * m_reader; | |
| 81 unsigned m_buffer_state, m_buffer_size; | |
| 82 bool m_eof; | |
| 83 unsigned char m_buffer[255]; | |
| 84 */ | |
| 85 | |
| 86 void stream_reader_chunk::g_skip(stream_reader * p_stream,abort_callback & p_abort) { | |
| 87 stream_reader_chunk(p_stream).flush(p_abort); | |
| 88 } | |
| 89 | |
| 90 | |
| 91 | |
| 92 static void fileSanitySeek(file::ptr f, pfc::array_t<uint8_t> const & content, size_t offset, abort_callback & aborter) { | |
| 93 const size_t readAmount = 64 * 1024; | |
| 94 pfc::array_staticsize_t<uint8_t> buf; buf.set_size_discard(readAmount); | |
| 95 f->seek(offset, aborter); | |
| 96 t_filesize positionGot = f->get_position(aborter); | |
| 97 if (positionGot != offset) { | |
| 98 FB2K_console_formatter() << "File sanity: at " << offset << " reported position became " << positionGot; | |
| 99 throw std::runtime_error("Seek test failure"); | |
| 100 } | |
| 101 size_t did = f->read(buf.get_ptr(), readAmount, aborter); | |
| 102 size_t expected = pfc::min_t<size_t>(readAmount, content.get_size() - offset); | |
| 103 if (expected != did) { | |
| 104 FB2K_console_formatter() << "File sanity: at " << offset << " bytes, expected read size of " << expected << ", got " << did; | |
| 105 if (did > expected) FB2K_console_formatter() << "Read past EOF"; | |
| 106 else FB2K_console_formatter() << "Premature EOF"; | |
| 107 throw std::runtime_error("Seek test failure"); | |
| 108 } | |
| 109 if (memcmp(buf.get_ptr(), content.get_ptr() + offset, did) != 0) { | |
| 110 FB2K_console_formatter() << "File sanity: data mismatch at " << offset << " - " << (offset + did) << " bytes"; | |
| 111 throw std::runtime_error("Seek test failure"); | |
| 112 } | |
| 113 positionGot = f->get_position(aborter); | |
| 114 if (positionGot != offset + did) { | |
| 115 FB2K_console_formatter() << "File sanity: at " << offset << "+" << did << "=" << (offset + did) << " reported position became " << positionGot; | |
| 116 throw std::runtime_error("Seek test failure"); | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 bool fb2kFileSelfTest(file::ptr f, abort_callback & aborter) { | |
| 121 try { | |
| 122 pfc::array_t<uint8_t> fileContent; | |
| 123 f->reopen(aborter); | |
| 124 f->read_till_eof(fileContent, aborter); | |
| 125 | |
| 126 { | |
| 127 t_filesize sizeClaimed = f->get_size(aborter); | |
| 128 if (sizeClaimed == filesize_invalid) { | |
| 129 FB2K_console_formatter() << "File sanity: file reports unknown size, actual size read is " << fileContent.get_size(); | |
| 130 } | |
| 131 else { | |
| 132 if (sizeClaimed != fileContent.get_size()) { | |
| 133 FB2K_console_formatter() << "File sanity: file reports size of " << sizeClaimed << ", actual size read is " << fileContent.get_size(); | |
| 134 throw std::runtime_error("File size mismatch"); | |
| 135 } | |
| 136 else { | |
| 137 FB2K_console_formatter() << "File sanity: file size check OK: " << sizeClaimed; | |
| 138 } | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 { | |
| 143 FB2K_console_formatter() << "File sanity: testing N-first-bytes reads..."; | |
| 144 const size_t sizeUpTo = pfc::min_t<size_t>(fileContent.get_size(), 1024 * 1024); | |
| 145 pfc::array_staticsize_t<uint8_t> buf1; | |
| 146 buf1.set_size_discard(sizeUpTo); | |
| 147 | |
| 148 for (size_t w = 1; w <= sizeUpTo; w <<= 1) { | |
| 149 f->reopen(aborter); | |
| 150 size_t did = f->read(buf1.get_ptr(), w, aborter); | |
| 151 if (did != w) { | |
| 152 FB2K_console_formatter() << "File sanity: premature EOF reading first " << w << " bytes, got " << did; | |
| 153 throw std::runtime_error("Premature EOF"); | |
| 154 } | |
| 155 if (memcmp(fileContent.get_ptr(), buf1.get_ptr(), did) != 0) { | |
| 156 FB2K_console_formatter() << "File sanity: file content mismatch reading first " << w << " bytes"; | |
| 157 throw std::runtime_error("File content mismatch"); | |
| 158 } | |
| 159 } | |
| 160 } | |
| 161 if (f->can_seek()) { | |
| 162 FB2K_console_formatter() << "File sanity: testing random access..."; | |
| 163 | |
| 164 { | |
| 165 size_t sizeUpTo = pfc::min_t<size_t>(fileContent.get_size(), 1024 * 1024); | |
| 166 for (size_t w = 1; w < sizeUpTo; w <<= 1) { | |
| 167 fileSanitySeek(f, fileContent, w, aborter); | |
| 168 } | |
| 169 fileSanitySeek(f, fileContent, fileContent.get_size(), aborter); | |
| 170 for (size_t w = 1; w < sizeUpTo; w <<= 1) { | |
| 171 fileSanitySeek(f, fileContent, fileContent.get_size() - w, aborter); | |
| 172 } | |
| 173 fileSanitySeek(f, fileContent, fileContent.get_size() / 2, aborter); | |
| 174 } | |
| 175 } | |
| 176 FB2K_console_formatter() << "File sanity test: all OK"; | |
| 177 return true; | |
| 178 } | |
| 179 catch (std::exception const & e) { | |
| 180 FB2K_console_formatter() << "File sanity test failure: " << e.what(); | |
| 181 return false; | |
| 182 } | |
| 183 } | |
| 184 | |
| 185 | |
| 186 namespace foobar2000_io { | |
| 187 void retryFileDelete(double timeout, abort_callback & a, std::function<void()> f) { | |
| 188 FB2K_RETRY_ON_EXCEPTION3(f(), a, timeout, exception_io_sharing_violation, exception_io_denied, exception_io_directory_not_empty); | |
| 189 } | |
| 190 void retryFileMove(double timeout, abort_callback & a, std::function<void() > f) { | |
| 191 FB2K_RETRY_FILE_MOVE( f(), a, timeout ); | |
| 192 } | |
| 193 void retryOnSharingViolation(double timeout, abort_callback & a, std::function<void() > f) { | |
| 194 FB2K_RETRY_ON_SHARING_VIOLATION(f(), a, timeout); | |
| 195 } | |
| 196 void retryOnSharingViolation(std::function<void() > f, double timeout, abort_callback & a) { | |
| 197 FB2K_RETRY_ON_SHARING_VIOLATION( f(), a, timeout ); | |
| 198 } | |
| 199 void listDirectory( const char * path, abort_callback & aborter, listDirectoryFunc_t func) { | |
| 200 listDirectoryCallbackImpl cb; cb.m_func = func; | |
| 201 filesystem::g_list_directory(path, cb, aborter); | |
| 202 } | |
| 203 | |
| 204 pfc::string8 stripParentFolders( const char * inPath ) { | |
| 205 PFC_ASSERT( strstr(inPath, "://" ) == nullptr || matchProtocol( inPath, "file" ) ); | |
| 206 | |
| 207 size_t prefixLen = pfc::string_find_first(inPath, "://"); | |
| 208 if ( prefixLen != pfc_infinite ) prefixLen += 3; | |
| 209 else prefixLen = 0; | |
| 210 | |
| 211 pfc::chain_list_v2_t<pfc::string_part_ref> segments; | |
| 212 | |
| 213 const char separator = | |
| 214 #ifdef _WIN32 | |
| 215 '\\' | |
| 216 #else | |
| 217 '/' | |
| 218 #endif | |
| 219 ; | |
| 220 const char strSeparator[] = {separator, 0}; | |
| 221 | |
| 222 pfc::splitStringByChar(segments, inPath + prefixLen, separator ); | |
| 223 for ( auto i = segments.first(); i.is_valid(); ) { | |
| 224 auto n = i; ++n; | |
| 225 if ( i->equals( "." ) ) { | |
| 226 segments.remove_single( i ); | |
| 227 } else if ( i->equals( ".." ) ) { | |
| 228 auto p = i; --p; | |
| 229 if ( p.is_valid() ) segments.remove_single( p ); | |
| 230 segments.remove_single( i ); | |
| 231 } | |
| 232 i = n; | |
| 233 } | |
| 234 pfc::string8 ret; | |
| 235 if ( prefixLen > 0 ) ret.add_string( inPath, prefixLen ); | |
| 236 bool bFirst = true; | |
| 237 for ( auto i = segments.first(); i.is_valid(); ++ i ) { | |
| 238 if (!bFirst) ret << strSeparator; | |
| 239 ret << *i; | |
| 240 bFirst = false; | |
| 241 } | |
| 242 return ret; | |
| 243 } | |
| 244 #ifdef _WIN32 | |
| 245 pfc::string8 winGetVolumePath(const char * fb2kPath) { | |
| 246 PFC_ASSERT(matchProtocol(fb2kPath, "file")); | |
| 247 pfc::string8 native; | |
| 248 if (!filesystem::g_get_native_path(fb2kPath, native)) throw pfc::exception_invalid_params(); | |
| 249 | |
| 250 TCHAR outBuffer[MAX_PATH+1] = {}; | |
| 251 WIN32_IO_OP( GetVolumePathName( pfc::stringcvt::string_os_from_utf8( native ), outBuffer, MAX_PATH ) ); | |
| 252 return pfc::stringcvt::string_utf8_from_os( outBuffer ).get_ptr(); | |
| 253 } | |
| 254 | |
| 255 DWORD winVolumeFlags( const char * fb2kPath ) { | |
| 256 PFC_ASSERT(matchProtocol(fb2kPath, "file")); | |
| 257 pfc::string8 native; | |
| 258 if (!filesystem::g_get_native_path(fb2kPath, native)) throw pfc::exception_invalid_params(); | |
| 259 | |
| 260 TCHAR outBuffer[MAX_PATH + 1] = {}; | |
| 261 WIN32_IO_OP(GetVolumePathName(pfc::stringcvt::string_os_from_utf8(native), outBuffer, MAX_PATH)); | |
| 262 | |
| 263 DWORD flags = 0; | |
| 264 WIN32_IO_OP(GetVolumeInformation(outBuffer, nullptr, 0, nullptr, nullptr, &flags, nullptr, 0)); | |
| 265 return flags; | |
| 266 } | |
| 267 #endif | |
| 268 } | |
| 269 | |
| 270 pfc::string8 file_path_canonical(const char* src) { | |
| 271 pfc::string8 ret; | |
| 272 filesystem::g_get_canonical_path(src, ret); | |
| 273 return ret; | |
| 274 } | |
| 275 | |
| 276 pfc::string8 file_path_display(const char* src) { | |
| 277 pfc::string8 ret; | |
| 278 filesystem::g_get_display_path(src, ret); | |
| 279 return ret; | |
| 280 } | |
| 281 | |
| 282 pfc::string8 fb2k::filename_ext( const char * path, filesystem::ptr & fs) { | |
| 283 if ( fs.is_empty() || ! fs->is_our_path( path ) ) { | |
| 284 fs = filesystem::tryGet( path ); | |
| 285 } | |
| 286 if ( fs.is_valid() ) return fs->extract_filename_ext( path ); | |
| 287 // UGLY FALLBACK | |
| 288 return pfc::string_filename_ext( path ); | |
| 289 | |
| 290 } | |
| 291 pfc::string8 fb2k::filename_ext( const char * path ) { | |
| 292 filesystem::ptr no_reuse; | |
| 293 return filename_ext( path, no_reuse ); | |
| 294 } | |
| 295 | |
| 296 pfc::string8 fb2k::filename( const char * path ) { | |
| 297 return pfc::remove_ext_v2( filename_ext( path ) ); | |
| 298 } |
