#pragma once #include namespace foobar2000_io { typedef std::function< void (const char *, t_filestats const & , bool ) > listDirectoryFunc_t; void listDirectory( const char * path, abort_callback & aborter, listDirectoryFunc_t func); #ifdef _WIN32 pfc::string8 stripParentFolders( const char * inPath ); #endif void retryOnSharingViolation( std::function f, double timeout, abort_callback & a); void retryOnSharingViolation( double timeout, abort_callback & a, std::function f); // **** WINDOWS SUCKS **** // Special version of retryOnSharingViolation with workarounds for known MoveFile() bugs. void retryFileMove( double timeout, abort_callback & a, std::function f); // **** WINDOWS SUCKS **** // Special version of retryOnSharingViolation with workarounds for known idiotic problems with folder removal. void retryFileDelete( double timeout, abort_callback & a, std::function f); class listDirectoryCallbackImpl : public directory_callback { public: listDirectoryCallbackImpl() {} listDirectoryCallbackImpl( listDirectoryFunc_t f ) : m_func(f) {} bool on_entry(filesystem * p_owner, abort_callback & p_abort, const char * p_url, bool p_is_subdirectory, const t_filestats & p_stats) { m_func(p_url, p_stats, p_is_subdirectory); return true; } listDirectoryFunc_t m_func; }; #ifdef _WIN32 pfc::string8 winGetVolumePath(const char * fb2kPath ); DWORD winVolumeFlags( const char * fb2kPath ); #endif } //helper class file_path_canonical { public: file_path_canonical(const char * src) {filesystem::g_get_canonical_path(src,m_data);} operator const char * () const {return m_data.get_ptr();} const char * get_ptr() const {return m_data.get_ptr();} t_size get_length() const {return m_data.get_length();} private: pfc::string8 m_data; }; class file_path_display { public: file_path_display(const char * src) {filesystem::g_get_display_path(src,m_data);} operator const char * () const {return m_data.get_ptr();} const char * get_ptr() const {return m_data.get_ptr();} t_size get_length() const {return m_data.get_length();} private: pfc::string8 m_data; }; class stream_reader_memblock_ref : public stream_reader { public: template stream_reader_memblock_ref(const t_array & p_array) : m_data(p_array.get_ptr()), m_data_size(p_array.get_size()), m_pointer(0) { pfc::assert_byte_type(); } stream_reader_memblock_ref(const void * p_data,t_size p_data_size) : m_data((const unsigned char*)p_data), m_data_size(p_data_size), m_pointer(0) {} stream_reader_memblock_ref() : m_data(NULL), m_data_size(0), m_pointer(0) {} template void set_data(const t_array & data) { pfc::assert_byte_type(); set_data(data.get_ptr(), data.get_size()); } void set_data(const void * data, t_size dataSize) { m_pointer = 0; m_data = reinterpret_cast(data); m_data_size = dataSize; } t_size read(void * p_buffer,t_size p_bytes,abort_callback & p_abort) { t_size delta = pfc::min_t(p_bytes, get_remaining()); memcpy(p_buffer,m_data+m_pointer,delta); m_pointer += delta; return delta; } void read_object(void * p_buffer,t_size p_bytes,abort_callback & p_abort) { if (p_bytes > get_remaining()) throw exception_io_data_truncation(); memcpy(p_buffer,m_data+m_pointer,p_bytes); m_pointer += p_bytes; } t_filesize skip(t_filesize p_bytes,abort_callback & p_abort) { t_size remaining = get_remaining(); if (p_bytes >= remaining) { m_pointer = m_data_size; return remaining; } else { m_pointer += (t_size)p_bytes; return p_bytes; } } void skip_object(t_filesize p_bytes,abort_callback & p_abort) { if (p_bytes > get_remaining()) { throw exception_io_data_truncation(); } else { m_pointer += (t_size)p_bytes; } } void seek_(t_size offset) { PFC_ASSERT( offset <= m_data_size ); m_pointer = offset; } const void * get_ptr_() const {return m_data + m_pointer;} t_size get_remaining() const {return m_data_size - m_pointer;} void reset() {m_pointer = 0;} private: const unsigned char * m_data; t_size m_data_size,m_pointer; }; class stream_writer_buffer_simple : public stream_writer { public: void write(const void * p_buffer,t_size p_bytes,abort_callback & p_abort) { p_abort.check(); t_size base = m_buffer.get_size(); if (base + p_bytes < base) throw std::bad_alloc(); m_buffer.set_size(base + p_bytes); memcpy( (t_uint8*) m_buffer.get_ptr() + base, p_buffer, p_bytes ); } typedef pfc::array_t t_buffer; pfc::array_t m_buffer; }; template class stream_writer_buffer_append_ref_t : public stream_writer { public: stream_writer_buffer_append_ref_t(t_storage & p_output) : m_output(p_output) {} void write(const void * p_buffer,t_size p_bytes,abort_callback & p_abort) { PFC_STATIC_ASSERT( sizeof(m_output[0]) == 1 ); p_abort.check(); t_size base = m_output.get_size(); if (base + p_bytes < base) throw std::bad_alloc(); m_output.set_size(base + p_bytes); memcpy( (t_uint8*) m_output.get_ptr() + base, p_buffer, p_bytes ); } private: t_storage & m_output; }; class stream_reader_limited_ref : public stream_reader { public: stream_reader_limited_ref(stream_reader * p_reader,t_filesize p_limit) : m_reader(p_reader), m_remaining(p_limit) {} t_size read(void * p_buffer,t_size p_bytes,abort_callback & p_abort) { if (p_bytes > m_remaining) p_bytes = (t_size)m_remaining; t_size done = m_reader->read(p_buffer,p_bytes,p_abort); m_remaining -= done; return done; } inline t_filesize get_remaining() const {return m_remaining;} t_filesize skip(t_filesize p_bytes,abort_callback & p_abort) { if (p_bytes > m_remaining) p_bytes = m_remaining; t_filesize done = m_reader->skip(p_bytes,p_abort); m_remaining -= done; return done; } void flush_remaining(abort_callback & p_abort) { if (m_remaining > 0) skip_object(m_remaining,p_abort); } private: stream_reader * m_reader; t_filesize m_remaining; }; class stream_writer_chunk_dwordheader : public stream_writer { public: stream_writer_chunk_dwordheader(const service_ptr_t & p_writer) : m_writer(p_writer) {} void initialize(abort_callback & p_abort) { m_headerposition = m_writer->get_position(p_abort); m_written = 0; m_writer->write_lendian_t((t_uint32)0,p_abort); } void finalize(abort_callback & p_abort) { t_filesize end_offset; end_offset = m_writer->get_position(p_abort); m_writer->seek(m_headerposition,p_abort); m_writer->write_lendian_t(pfc::downcast_guarded(m_written),p_abort); m_writer->seek(end_offset,p_abort); } void write(const void * p_buffer,t_size p_bytes,abort_callback & p_abort) { m_writer->write(p_buffer,p_bytes,p_abort); m_written += p_bytes; } private: service_ptr_t m_writer; t_filesize m_headerposition; t_filesize m_written; }; class stream_writer_chunk : public stream_writer { public: stream_writer_chunk(stream_writer * p_writer) : m_writer(p_writer), m_buffer_state(0) {} void write(const void * p_buffer,t_size p_bytes,abort_callback & p_abort); void flush(abort_callback & p_abort);//must be called after writing before object is destroyed private: stream_writer * m_writer; unsigned m_buffer_state; unsigned char m_buffer[255]; }; class stream_reader_chunk : public stream_reader { public: stream_reader_chunk(stream_reader * p_reader) : m_reader(p_reader), m_buffer_state(0), m_buffer_size(0), m_eof(false) {} t_size read(void * p_buffer,t_size p_bytes,abort_callback & p_abort); void flush(abort_callback & p_abort);//must be called after reading before object is destroyed static void g_skip(stream_reader * p_stream,abort_callback & p_abort); private: stream_reader * m_reader; t_size m_buffer_state, m_buffer_size; bool m_eof; unsigned char m_buffer[255]; }; class stream_reader_dummy : public stream_reader { t_size read(void * p_buffer,t_size p_bytes,abort_callback & p_abort) {return 0;} }; template class stream_reader_formatter { public: stream_reader_formatter(stream_reader & p_stream,abort_callback & p_abort) : m_stream(p_stream), m_abort(p_abort) {} template void read_int(t_int & p_out) { if (isBigEndian) m_stream.read_bendian_t(p_out,m_abort); else m_stream.read_lendian_t(p_out,m_abort); } void read_raw(void * p_buffer,t_size p_bytes) { m_stream.read_object(p_buffer,p_bytes,m_abort); } void skip(t_size p_bytes) {m_stream.skip_object(p_bytes,m_abort);} template void read_raw(TArray& data) { pfc::assert_byte_type(); read_raw(data.get_ptr(),data.get_size()); } template void read_byte_block(TArray & data) { pfc::assert_byte_type(); t_uint32 size; read_int(size); data.set_size(size); read_raw(data); } template void read_array(TArray & data) { t_uint32 size; *this >> size; data.set_size(size); for(t_uint32 walk = 0; walk < size; ++walk) *this >> data[walk]; } void read_string_nullterm( pfc::string_base & out ) { m_stream.read_string_nullterm( out, m_abort ); } stream_reader & m_stream; abort_callback & m_abort; }; template class stream_writer_formatter { public: stream_writer_formatter(stream_writer & p_stream,abort_callback & p_abort) : m_stream(p_stream), m_abort(p_abort) {} template void write_int(t_int p_int) { if (isBigEndian) m_stream.write_bendian_t(p_int,m_abort); else m_stream.write_lendian_t(p_int,m_abort); } void write_raw(const void * p_buffer,t_size p_bytes) { m_stream.write_object(p_buffer,p_bytes,m_abort); } template void write_raw(const TArray& data) { pfc::assert_byte_type(); write_raw(data.get_ptr(),data.get_size()); } template void write_byte_block(const TArray& data) { pfc::assert_byte_type(); write_int( pfc::downcast_guarded(data.get_size()) ); write_raw( data ); } template void write_array(const TArray& data) { const t_uint32 size = pfc::downcast_guarded(data.get_size()); *this << size; for(t_uint32 walk = 0; walk < size; ++walk) *this << data[walk]; } void write_string(const char * str) { const t_size len = strlen(str); *this << pfc::downcast_guarded(len); write_raw(str, len); } void write_string(const char * str, t_size len_) { const t_size len = pfc::strlen_max(str, len_); *this << pfc::downcast_guarded(len); write_raw(str, len); } void write_string_nullterm( const char * str ) { this->write_raw( str, strlen(str)+1 ); } stream_writer & m_stream; abort_callback & m_abort; }; #define __DECLARE_INT_OVERLOADS(TYPE) \ template inline stream_reader_formatter & operator>>(stream_reader_formatter & p_stream,TYPE & p_int) {typename pfc::sized_int_t::t_unsigned temp;p_stream.read_int(temp); p_int = (TYPE) temp; return p_stream;} \ template inline stream_writer_formatter & operator<<(stream_writer_formatter & p_stream,TYPE p_int) {p_stream.write_int((typename pfc::sized_int_t::t_unsigned)p_int); return p_stream;} __DECLARE_INT_OVERLOADS(char); __DECLARE_INT_OVERLOADS(signed char); __DECLARE_INT_OVERLOADS(unsigned char); __DECLARE_INT_OVERLOADS(signed short); __DECLARE_INT_OVERLOADS(unsigned short); __DECLARE_INT_OVERLOADS(signed int); __DECLARE_INT_OVERLOADS(unsigned int); __DECLARE_INT_OVERLOADS(signed long); __DECLARE_INT_OVERLOADS(unsigned long); __DECLARE_INT_OVERLOADS(signed long long); __DECLARE_INT_OVERLOADS(unsigned long long); __DECLARE_INT_OVERLOADS(wchar_t); #undef __DECLARE_INT_OVERLOADS template class _IsTypeByte { public: enum {value = pfc::is_same_type::value || pfc::is_same_type::value || pfc::is_same_type::value}; }; template stream_reader_formatter & operator>>(stream_reader_formatter & p_stream,TVal (& p_array)[Count]) { if (_IsTypeByte::value) { p_stream.read_raw(p_array,Count); } else { for(t_size walk = 0; walk < Count; ++walk) p_stream >> p_array[walk]; } return p_stream; } template stream_writer_formatter & operator<<(stream_writer_formatter & p_stream,TVal const (& p_array)[Count]) { if (_IsTypeByte::value) { p_stream.write_raw(p_array,Count); } else { for(t_size walk = 0; walk < Count; ++walk) p_stream << p_array[walk]; } return p_stream; } #define FB2K_STREAM_READER_OVERLOAD(type) \ template stream_reader_formatter & operator>>(stream_reader_formatter & stream,type & value) #define FB2K_STREAM_WRITER_OVERLOAD(type) \ template stream_writer_formatter & operator<<(stream_writer_formatter & stream,const type & value) FB2K_STREAM_READER_OVERLOAD(GUID) { return stream >> value.Data1 >> value.Data2 >> value.Data3 >> value.Data4; } FB2K_STREAM_WRITER_OVERLOAD(GUID) { return stream << value.Data1 << value.Data2 << value.Data3 << value.Data4; } FB2K_STREAM_READER_OVERLOAD(pfc::string) { t_uint32 len; stream >> len; value = stream.m_stream.read_string_ex(len,stream.m_abort); return stream; } FB2K_STREAM_WRITER_OVERLOAD(pfc::string) { stream << pfc::downcast_guarded(value.length()); stream.write_raw(value.ptr(),value.length()); return stream; } FB2K_STREAM_READER_OVERLOAD(pfc::string_base) { stream.m_stream.read_string(value, stream.m_abort); return stream; } FB2K_STREAM_WRITER_OVERLOAD(pfc::string_base) { const char * val = value.get_ptr(); const t_size len = strlen(val); stream << pfc::downcast_guarded(len); stream.write_raw(val,len); return stream; } FB2K_STREAM_WRITER_OVERLOAD(float) { union { float f; t_uint32 i; } u; u.f = value; return stream << u.i; } FB2K_STREAM_READER_OVERLOAD(float) { union { float f; t_uint32 i;} u; stream >> u.i; value = u.f; return stream; } FB2K_STREAM_WRITER_OVERLOAD(double) { union { double f; t_uint64 i; } u; u.f = value; return stream << u.i; } FB2K_STREAM_READER_OVERLOAD(double) { union { double f; t_uint64 i;} u; stream >> u.i; value = u.f; return stream; } FB2K_STREAM_WRITER_OVERLOAD(bool) { t_uint8 temp = value ? 1 : 0; return stream << temp; } FB2K_STREAM_READER_OVERLOAD(bool) { t_uint8 temp; stream >> temp; value = temp != 0; return stream; } template class stream_writer_formatter_simple : public stream_writer_formatter { public: stream_writer_formatter_simple() : stream_writer_formatter(_m_stream,fb2k::noAbort), m_buffer(_m_stream.m_buffer) {} typedef stream_writer_buffer_simple::t_buffer t_buffer; t_buffer & m_buffer; private: stream_writer_buffer_simple _m_stream; }; template class stream_reader_formatter_simple_ref : public stream_reader_formatter { public: stream_reader_formatter_simple_ref(const void * source, t_size sourceSize) : stream_reader_formatter(_m_stream,fb2k::noAbort), _m_stream(source,sourceSize) {} template stream_reader_formatter_simple_ref(const TSource& source) : stream_reader_formatter(_m_stream,fb2k::noAbort), _m_stream(source) {} stream_reader_formatter_simple_ref() : stream_reader_formatter(_m_stream,fb2k::noAbort) {} void set_data(const void * source, t_size sourceSize) {_m_stream.set_data(source,sourceSize);} template void set_data(const TSource & source) {_m_stream.set_data(source);} void reset() {_m_stream.reset();} t_size get_remaining() {return _m_stream.get_remaining();} const void * get_ptr_() const {return _m_stream.get_ptr_();} private: stream_reader_memblock_ref _m_stream; }; template class stream_reader_formatter_simple : public stream_reader_formatter_simple_ref { public: stream_reader_formatter_simple() {} stream_reader_formatter_simple(const void * source, t_size sourceSize) {set_data(source,sourceSize);} template stream_reader_formatter_simple(const TSource & source) {set_data(source);} void set_data(const void * source, t_size sourceSize) { m_content.set_data_fromptr(reinterpret_cast(source), sourceSize); onContentChange(); } template void set_data(const TSource & source) { m_content = source; onContentChange(); } private: void onContentChange() { stream_reader_formatter_simple_ref::set_data(m_content); } pfc::array_t m_content; }; template class _stream_reader_formatter_translator { public: _stream_reader_formatter_translator(stream_reader_formatter & stream) : m_stream(stream) {} typedef _stream_reader_formatter_translator t_self; template t_self & operator||(t_what & out) {m_stream >> out; return *this;} private: stream_reader_formatter & m_stream; }; template class _stream_writer_formatter_translator { public: _stream_writer_formatter_translator(stream_writer_formatter & stream) : m_stream(stream) {} typedef _stream_writer_formatter_translator t_self; template t_self & operator||(const t_what & in) {m_stream << in; return *this;} private: stream_writer_formatter & m_stream; }; #define FB2K_STREAM_RECORD_OVERLOAD(type, code) \ FB2K_STREAM_READER_OVERLOAD(type) { \ _stream_reader_formatter_translator streamEx(stream); \ streamEx || code; \ return stream; \ } \ FB2K_STREAM_WRITER_OVERLOAD(type) { \ _stream_writer_formatter_translator streamEx(stream); \ streamEx || code; \ return stream; \ } #define FB2K_RETRY_ON_EXCEPTION(OP, ABORT, TIMEOUT, EXCEPTION) \ { \ pfc::lores_timer timer; timer.start(); \ for(;;) { \ try { {OP;} break; } \ catch(EXCEPTION) { if (timer.query() > TIMEOUT) throw;} \ ABORT.sleep(0.05); \ } \ } #define FB2K_RETRY_ON_EXCEPTION2(OP, ABORT, TIMEOUT, EXCEPTION1, EXCEPTION2) \ { \ pfc::lores_timer timer; timer.start(); \ for(;;) { \ try { {OP;} break; } \ catch(EXCEPTION1) { if (timer.query() > TIMEOUT) throw;} \ catch(EXCEPTION2) { if (timer.query() > TIMEOUT) throw;} \ ABORT.sleep(0.05); \ } \ } #define FB2K_RETRY_ON_EXCEPTION3(OP, ABORT, TIMEOUT, EXCEPTION1, EXCEPTION2, EXCEPTION3) \ { \ pfc::lores_timer timer; timer.start(); \ for(;;) { \ try { {OP;} break; } \ catch(EXCEPTION1) { if (timer.query() > TIMEOUT) throw;} \ catch(EXCEPTION2) { if (timer.query() > TIMEOUT) throw;} \ catch(EXCEPTION3) { if (timer.query() > TIMEOUT) throw;} \ ABORT.sleep(0.05); \ } \ } #define FB2K_RETRY_ON_SHARING_VIOLATION(OP, ABORT, TIMEOUT) FB2K_RETRY_ON_EXCEPTION(OP, ABORT, TIMEOUT, exception_io_sharing_violation) // **** WINDOWS SUCKS **** // File move ops must be retried on all these because you get access-denied when someone is holding open handles to something you're trying to move, or already-exists on something you just told Windows to move away #define FB2K_RETRY_FILE_MOVE(OP, ABORT, TIMEOUT) FB2K_RETRY_ON_EXCEPTION3(OP, ABORT, TIMEOUT, exception_io_sharing_violation, exception_io_denied, exception_io_already_exists) class fileRestorePositionScope { public: fileRestorePositionScope(file::ptr f, abort_callback & a) : m_file(f), m_abort(a) { m_offset = f->get_position(a); } ~fileRestorePositionScope() { try { if (!m_abort.is_aborting()) m_file->seek(m_offset, m_abort); } catch(...) {} } private: file::ptr m_file; t_filesize m_offset; abort_callback & m_abort; }; //! Debug self-test function for testing a file object implementation, performs various behavior validity checks, random access etc. Output goes to fb2k console. //! Returns true on success, false on failure (buggy file object implementation). bool fb2kFileSelfTest(file::ptr f, abort_callback & aborter);