|
1
|
1 #pragma once
|
|
|
2
|
|
|
3 #include <functional>
|
|
|
4 #include "filesystem.h"
|
|
|
5
|
|
|
6 namespace foobar2000_io {
|
|
|
7 typedef std::function< void (const char *, t_filestats const & , bool ) > listDirectoryFunc_t;
|
|
|
8 void listDirectory( const char * path, abort_callback & aborter, listDirectoryFunc_t func);
|
|
|
9
|
|
|
10 #ifdef _WIN32
|
|
|
11 pfc::string8 stripParentFolders( const char * inPath );
|
|
|
12 #endif
|
|
|
13
|
|
|
14 void retryOnSharingViolation( std::function<void () > f, double timeout, abort_callback & a);
|
|
|
15 void retryOnSharingViolation( double timeout, abort_callback & a, std::function<void() > f);
|
|
|
16
|
|
|
17 // **** WINDOWS SUCKS ****
|
|
|
18 // Special version of retryOnSharingViolation with workarounds for known MoveFile() bugs.
|
|
|
19 void retryFileMove( double timeout, abort_callback & a, std::function<void()> f);
|
|
|
20
|
|
|
21 // **** WINDOWS SUCKS ****
|
|
|
22 // Special version of retryOnSharingViolation with workarounds for known idiotic problems with folder removal.
|
|
|
23 void retryFileDelete( double timeout, abort_callback & a, std::function<void()> f);
|
|
|
24
|
|
|
25 class listDirectoryCallbackImpl : public directory_callback {
|
|
|
26 public:
|
|
|
27 listDirectoryCallbackImpl() {}
|
|
|
28 listDirectoryCallbackImpl( listDirectoryFunc_t f ) : m_func(f) {}
|
|
|
29 bool on_entry(filesystem *, abort_callback &, const char * p_url, bool p_is_subdirectory, const t_filestats & p_stats) override {
|
|
|
30 m_func(p_url, p_stats, p_is_subdirectory);
|
|
|
31 return true;
|
|
|
32 }
|
|
|
33 listDirectoryFunc_t m_func;
|
|
|
34 };
|
|
|
35
|
|
|
36 #ifdef _WIN32
|
|
|
37 pfc::string8 winGetVolumePath(const char * fb2kPath );
|
|
|
38 DWORD winVolumeFlags( const char * fb2kPath );
|
|
|
39 #endif
|
|
|
40 }
|
|
|
41
|
|
|
42
|
|
|
43 pfc::string8 file_path_canonical(const char* src);
|
|
|
44 pfc::string8 file_path_display(const char* src);
|
|
|
45
|
|
|
46 namespace fb2k {
|
|
|
47 //! Sane replacement for pfc::string_filename_ext(), which isn't safe to use in cross-platform code.
|
|
|
48 //! @returns Filename with extension extracted from path.
|
|
|
49 pfc::string8 filename_ext( const char * path );
|
|
|
50 pfc::string8 filename_ext( const char * path, filesystem::ptr & fs_reuse);
|
|
|
51 //! Sane replacement for pfc::string_filename(), which isn't safe to use in cross-platform code
|
|
|
52 //! @returns Filename without extension extracted from path.
|
|
|
53 pfc::string8 filename( const char * path );
|
|
|
54 }
|
|
|
55
|
|
|
56 class stream_reader_memblock_ref : public stream_reader
|
|
|
57 {
|
|
|
58 public:
|
|
|
59 template<typename t_array> 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) {
|
|
|
60 pfc::assert_byte_type<typename t_array::t_item>();
|
|
|
61 }
|
|
|
62 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) {}
|
|
|
63 stream_reader_memblock_ref() : m_data(NULL), m_data_size(0), m_pointer(0) {}
|
|
|
64
|
|
|
65 template<typename t_array> void set_data(const t_array & data) {
|
|
|
66 pfc::assert_byte_type<typename t_array::t_item>();
|
|
|
67 set_data(data.get_ptr(), data.get_size());
|
|
|
68 }
|
|
|
69
|
|
|
70 void set_data(const void * data, t_size dataSize) {
|
|
|
71 m_pointer = 0;
|
|
|
72 m_data = reinterpret_cast<const unsigned char*>(data);
|
|
|
73 m_data_size = dataSize;
|
|
|
74 }
|
|
|
75
|
|
|
76 t_size read(void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
|
|
|
77 p_abort.check();
|
|
|
78 t_size delta = pfc::min_t(p_bytes, get_remaining());
|
|
|
79 memcpy(p_buffer,m_data+m_pointer,delta);
|
|
|
80 m_pointer += delta;
|
|
|
81 return delta;
|
|
|
82 }
|
|
|
83 void read_object(void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
|
|
|
84 p_abort.check();
|
|
|
85 if (p_bytes > get_remaining()) throw exception_io_data_truncation();
|
|
|
86 memcpy(p_buffer,m_data+m_pointer,p_bytes);
|
|
|
87 m_pointer += p_bytes;
|
|
|
88 }
|
|
|
89 t_filesize skip(t_filesize p_bytes,abort_callback & p_abort) {
|
|
|
90 p_abort.check();
|
|
|
91 t_size remaining = get_remaining();
|
|
|
92 if (p_bytes >= remaining) {
|
|
|
93 m_pointer = m_data_size; return remaining;
|
|
|
94 } else {
|
|
|
95 m_pointer += (t_size)p_bytes; return p_bytes;
|
|
|
96 }
|
|
|
97 }
|
|
|
98 void skip_object(t_filesize p_bytes,abort_callback & p_abort) {
|
|
|
99 p_abort.check();
|
|
|
100 if (p_bytes > get_remaining()) {
|
|
|
101 throw exception_io_data_truncation();
|
|
|
102 } else {
|
|
|
103 m_pointer += (t_size)p_bytes;
|
|
|
104 }
|
|
|
105 }
|
|
|
106 void seek_(t_size offset) {
|
|
|
107 PFC_ASSERT( offset <= m_data_size );
|
|
|
108 m_pointer = offset;
|
|
|
109 }
|
|
|
110 const void * get_ptr_() const {return m_data + m_pointer;}
|
|
|
111 t_size get_remaining() const {return m_data_size - m_pointer;}
|
|
|
112 void reset() {m_pointer = 0;}
|
|
|
113 private:
|
|
|
114 const unsigned char * m_data;
|
|
|
115 t_size m_data_size,m_pointer;
|
|
|
116 };
|
|
|
117
|
|
|
118 template<typename buffer_t>
|
|
|
119 class stream_writer_buffer_simple_t : public stream_writer {
|
|
|
120 public:
|
|
|
121 void write(const void * p_buffer, t_size p_bytes, abort_callback & p_abort) {
|
|
|
122 p_abort.check();
|
|
|
123 t_size base = m_buffer.get_size();
|
|
|
124 if (base + p_bytes < base) throw std::bad_alloc();
|
|
|
125 m_buffer.set_size(base + p_bytes);
|
|
|
126 memcpy((t_uint8*)m_buffer.get_ptr() + base, p_buffer, p_bytes);
|
|
|
127 }
|
|
|
128 typedef buffer_t t_buffer; // other classes reference this
|
|
|
129 t_buffer m_buffer;
|
|
|
130 };
|
|
|
131
|
|
|
132 typedef stream_writer_buffer_simple_t< pfc::array_t<t_uint8, pfc::alloc_fast> > stream_writer_buffer_simple;
|
|
|
133
|
|
|
134 template<class t_storage>
|
|
|
135 class stream_writer_buffer_append_ref_t : public stream_writer
|
|
|
136 {
|
|
|
137 public:
|
|
|
138 stream_writer_buffer_append_ref_t(t_storage & p_output) : m_output(p_output) {}
|
|
|
139 void write(const void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
|
|
|
140 PFC_STATIC_ASSERT( sizeof(m_output[0]) == 1 );
|
|
|
141 p_abort.check();
|
|
|
142 t_size base = m_output.get_size();
|
|
|
143 if (base + p_bytes < base) throw std::bad_alloc();
|
|
|
144 m_output.set_size(base + p_bytes);
|
|
|
145 memcpy( (t_uint8*) m_output.get_ptr() + base, p_buffer, p_bytes );
|
|
|
146 }
|
|
|
147 private:
|
|
|
148 t_storage & m_output;
|
|
|
149 };
|
|
|
150
|
|
|
151 class stream_reader_limited_ref : public stream_reader {
|
|
|
152 public:
|
|
|
153 stream_reader_limited_ref(stream_reader * p_reader,t_filesize p_limit) : m_reader(p_reader), m_remaining(p_limit) {}
|
|
|
154
|
|
|
155 t_size read(void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
|
|
|
156 if (p_bytes > m_remaining) p_bytes = (t_size)m_remaining;
|
|
|
157
|
|
|
158 t_size done = m_reader->read(p_buffer,p_bytes,p_abort);
|
|
|
159 m_remaining -= done;
|
|
|
160 return done;
|
|
|
161 }
|
|
|
162
|
|
|
163 inline t_filesize get_remaining() const {return m_remaining;}
|
|
|
164
|
|
|
165 t_filesize skip(t_filesize p_bytes,abort_callback & p_abort) {
|
|
|
166 if (p_bytes > m_remaining) p_bytes = m_remaining;
|
|
|
167 t_filesize done = m_reader->skip(p_bytes,p_abort);
|
|
|
168 m_remaining -= done;
|
|
|
169 return done;
|
|
|
170 }
|
|
|
171
|
|
|
172 void flush_remaining(abort_callback & p_abort) {
|
|
|
173 if (m_remaining > 0) skip_object(m_remaining,p_abort);
|
|
|
174 }
|
|
|
175
|
|
|
176 private:
|
|
|
177 stream_reader * m_reader;
|
|
|
178 t_filesize m_remaining;
|
|
|
179 };
|
|
|
180
|
|
|
181 class stream_writer_chunk_dwordheader : public stream_writer
|
|
|
182 {
|
|
|
183 public:
|
|
|
184 stream_writer_chunk_dwordheader(const service_ptr_t<file> & p_writer) : m_writer(p_writer) {}
|
|
|
185
|
|
|
186 void initialize(abort_callback & p_abort) {
|
|
|
187 m_headerposition = m_writer->get_position(p_abort);
|
|
|
188 m_written = 0;
|
|
|
189 m_writer->write_lendian_t((t_uint32)0,p_abort);
|
|
|
190 }
|
|
|
191
|
|
|
192 void finalize(abort_callback & p_abort) {
|
|
|
193 t_filesize end_offset;
|
|
|
194 end_offset = m_writer->get_position(p_abort);
|
|
|
195 m_writer->seek(m_headerposition,p_abort);
|
|
|
196 m_writer->write_lendian_t(pfc::downcast_guarded<t_uint32>(m_written),p_abort);
|
|
|
197 m_writer->seek(end_offset,p_abort);
|
|
|
198 }
|
|
|
199
|
|
|
200 void write(const void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
|
|
|
201 m_writer->write(p_buffer,p_bytes,p_abort);
|
|
|
202 m_written += p_bytes;
|
|
|
203 }
|
|
|
204
|
|
|
205 private:
|
|
|
206 service_ptr_t<file> m_writer;
|
|
|
207 t_filesize m_headerposition;
|
|
|
208 t_filesize m_written;
|
|
|
209 };
|
|
|
210
|
|
|
211 class stream_writer_chunk : public stream_writer
|
|
|
212 {
|
|
|
213 public:
|
|
|
214 stream_writer_chunk(stream_writer * p_writer) : m_writer(p_writer), m_buffer_state(0) {}
|
|
|
215
|
|
|
216 void write(const void * p_buffer,t_size p_bytes,abort_callback & p_abort);
|
|
|
217
|
|
|
218 void flush(abort_callback & p_abort);//must be called after writing before object is destroyed
|
|
|
219
|
|
|
220 private:
|
|
|
221 stream_writer * m_writer;
|
|
|
222 unsigned m_buffer_state;
|
|
|
223 unsigned char m_buffer[255];
|
|
|
224 };
|
|
|
225
|
|
|
226 class stream_reader_chunk : public stream_reader
|
|
|
227 {
|
|
|
228 public:
|
|
|
229 stream_reader_chunk(stream_reader * p_reader) : m_reader(p_reader), m_buffer_state(0), m_buffer_size(0), m_eof(false) {}
|
|
|
230
|
|
|
231 t_size read(void * p_buffer,t_size p_bytes,abort_callback & p_abort);
|
|
|
232
|
|
|
233 void flush(abort_callback & p_abort);//must be called after reading before object is destroyed
|
|
|
234
|
|
|
235 static void g_skip(stream_reader * p_stream,abort_callback & p_abort);
|
|
|
236
|
|
|
237 private:
|
|
|
238 stream_reader * m_reader;
|
|
|
239 t_size m_buffer_state, m_buffer_size;
|
|
|
240 bool m_eof;
|
|
|
241 unsigned char m_buffer[255];
|
|
|
242 };
|
|
|
243
|
|
|
244 class stream_reader_dummy : public stream_reader { t_size read(void *,t_size,abort_callback &) override {return 0;} };
|
|
|
245
|
|
|
246
|
|
|
247
|
|
|
248
|
|
|
249
|
|
|
250
|
|
|
251
|
|
|
252
|
|
|
253
|
|
|
254
|
|
|
255
|
|
|
256
|
|
|
257
|
|
|
258
|
|
|
259
|
|
|
260
|
|
|
261
|
|
|
262
|
|
|
263 template<bool isBigEndian = false> class stream_reader_formatter {
|
|
|
264 public:
|
|
|
265 stream_reader_formatter(stream_reader & p_stream,abort_callback & p_abort) : m_stream(p_stream), m_abort(p_abort) {}
|
|
|
266
|
|
|
267 template<typename t_int> void read_int(t_int & p_out) {
|
|
|
268 if (isBigEndian) m_stream.read_bendian_t(p_out,m_abort);
|
|
|
269 else m_stream.read_lendian_t(p_out,m_abort);
|
|
|
270 }
|
|
|
271
|
|
|
272 void read_raw(void * p_buffer,t_size p_bytes) {
|
|
|
273 m_stream.read_object(p_buffer,p_bytes,m_abort);
|
|
|
274 }
|
|
|
275
|
|
|
276 void skip(t_size p_bytes) {m_stream.skip_object(p_bytes,m_abort);}
|
|
|
277
|
|
|
278 template<typename TArray> void read_raw(TArray& data) {
|
|
|
279 pfc::assert_byte_type<typename TArray::t_item>();
|
|
|
280 read_raw(data.get_ptr(),data.get_size());
|
|
|
281 }
|
|
|
282 template<typename TArray> void read_byte_block(TArray & data) {
|
|
|
283 pfc::assert_byte_type<typename TArray::t_item>();
|
|
|
284 t_uint32 size; read_int(size); data.set_size(size);
|
|
|
285 read_raw(data);
|
|
|
286 }
|
|
|
287 template<typename TArray> void read_array(TArray & data) {
|
|
|
288 t_uint32 size; *this >> size; data.set_size(size);
|
|
|
289 for(t_uint32 walk = 0; walk < size; ++walk) *this >> data[walk];
|
|
|
290 }
|
|
|
291 void read_string_nullterm( pfc::string_base & ret ) {
|
|
|
292 m_stream.read_string_nullterm( ret, m_abort );
|
|
|
293 }
|
|
|
294 pfc::string8 read_string_nullterm() {
|
|
|
295 pfc::string8 ret; this->read_string_nullterm(ret); return ret;
|
|
|
296 }
|
|
|
297 pfc::string8 read_string() {
|
|
|
298 return m_stream.read_string(m_abort);
|
|
|
299 }
|
|
|
300 stream_reader & m_stream;
|
|
|
301 abort_callback & m_abort;
|
|
|
302 };
|
|
|
303
|
|
|
304 template<bool isBigEndian = false> class stream_writer_formatter {
|
|
|
305 public:
|
|
|
306 stream_writer_formatter(stream_writer & p_stream,abort_callback & p_abort) : m_stream(p_stream), m_abort(p_abort) {}
|
|
|
307
|
|
|
308 template<typename t_int> void write_int(t_int p_int) {
|
|
|
309 if (isBigEndian) m_stream.write_bendian_t(p_int,m_abort);
|
|
|
310 else m_stream.write_lendian_t(p_int,m_abort);
|
|
|
311 }
|
|
|
312
|
|
|
313 void write_raw(const void * p_buffer,t_size p_bytes) {
|
|
|
314 m_stream.write_object(p_buffer,p_bytes,m_abort);
|
|
|
315 }
|
|
|
316 template<typename TArray> void write_raw(const TArray& data) {
|
|
|
317 pfc::assert_byte_type<typename TArray::t_item>();
|
|
|
318 write_raw(data.get_ptr(),data.get_size());
|
|
|
319 }
|
|
|
320
|
|
|
321 template<typename TArray> void write_byte_block(const TArray& data) {
|
|
|
322 pfc::assert_byte_type<typename TArray::t_item>();
|
|
|
323 write_int( pfc::downcast_guarded<t_uint32>(data.get_size()) );
|
|
|
324 write_raw( data );
|
|
|
325 }
|
|
|
326 template<typename TArray> void write_array(const TArray& data) {
|
|
|
327 const t_uint32 size = pfc::downcast_guarded<t_uint32>(data.get_size());
|
|
|
328 *this << size;
|
|
|
329 for(t_uint32 walk = 0; walk < size; ++walk) *this << data[walk];
|
|
|
330 }
|
|
|
331
|
|
|
332 void write_string(const char * str) {
|
|
|
333 const t_size len = strlen(str);
|
|
|
334 *this << pfc::downcast_guarded<t_uint32>(len);
|
|
|
335 write_raw(str, len);
|
|
|
336 }
|
|
|
337 void write_string(const char * str, t_size len_) {
|
|
|
338 const t_size len = pfc::strlen_max(str, len_);
|
|
|
339 *this << pfc::downcast_guarded<t_uint32>(len);
|
|
|
340 write_raw(str, len);
|
|
|
341 }
|
|
|
342 void write_string_nullterm( const char * str ) {
|
|
|
343 this->write_raw( str, strlen(str)+1 );
|
|
|
344 }
|
|
|
345
|
|
|
346 stream_writer & m_stream;
|
|
|
347 abort_callback & m_abort;
|
|
|
348 };
|
|
|
349
|
|
|
350
|
|
|
351 #define __DECLARE_INT_OVERLOADS(TYPE) \
|
|
|
352 template<bool isBigEndian> inline stream_reader_formatter<isBigEndian> & operator>>(stream_reader_formatter<isBigEndian> & p_stream,TYPE & p_int) {typename pfc::sized_int_t<sizeof(TYPE)>::t_unsigned temp;p_stream.read_int(temp); p_int = (TYPE) temp; return p_stream;} \
|
|
|
353 template<bool isBigEndian> inline stream_writer_formatter<isBigEndian> & operator<<(stream_writer_formatter<isBigEndian> & p_stream,TYPE p_int) {p_stream.write_int((typename pfc::sized_int_t<sizeof(TYPE)>::t_unsigned)p_int); return p_stream;}
|
|
|
354
|
|
|
355 __DECLARE_INT_OVERLOADS(char);
|
|
|
356 __DECLARE_INT_OVERLOADS(signed char);
|
|
|
357 __DECLARE_INT_OVERLOADS(unsigned char);
|
|
|
358 __DECLARE_INT_OVERLOADS(signed short);
|
|
|
359 __DECLARE_INT_OVERLOADS(unsigned short);
|
|
|
360
|
|
|
361 __DECLARE_INT_OVERLOADS(signed int);
|
|
|
362 __DECLARE_INT_OVERLOADS(unsigned int);
|
|
|
363
|
|
|
364 __DECLARE_INT_OVERLOADS(signed long);
|
|
|
365 __DECLARE_INT_OVERLOADS(unsigned long);
|
|
|
366
|
|
|
367 __DECLARE_INT_OVERLOADS(signed long long);
|
|
|
368 __DECLARE_INT_OVERLOADS(unsigned long long);
|
|
|
369
|
|
|
370 __DECLARE_INT_OVERLOADS(wchar_t);
|
|
|
371
|
|
|
372
|
|
|
373 #undef __DECLARE_INT_OVERLOADS
|
|
|
374
|
|
|
375 template<typename TVal> class _IsTypeByte {
|
|
|
376 public:
|
|
|
377 enum {value = pfc::is_same_type<TVal,char>::value || pfc::is_same_type<TVal,unsigned char>::value || pfc::is_same_type<TVal,signed char>::value};
|
|
|
378 };
|
|
|
379
|
|
|
380 template<bool isBigEndian,typename TVal,size_t Count> stream_reader_formatter<isBigEndian> & operator>>(stream_reader_formatter<isBigEndian> & p_stream,TVal (& p_array)[Count]) {
|
|
|
381 if constexpr (_IsTypeByte<TVal>::value) {
|
|
|
382 p_stream.read_raw(p_array,Count);
|
|
|
383 } else {
|
|
|
384 for(t_size walk = 0; walk < Count; ++walk) p_stream >> p_array[walk];
|
|
|
385 }
|
|
|
386 return p_stream;
|
|
|
387 }
|
|
|
388
|
|
|
389 template<bool isBigEndian,typename TVal,size_t Count> stream_writer_formatter<isBigEndian> & operator<<(stream_writer_formatter<isBigEndian> & p_stream,TVal const (& p_array)[Count]) {
|
|
|
390 if constexpr (_IsTypeByte<TVal>::value) {
|
|
|
391 p_stream.write_raw(p_array,Count);
|
|
|
392 } else {
|
|
|
393 for(t_size walk = 0; walk < Count; ++walk) p_stream << p_array[walk];
|
|
|
394 }
|
|
|
395 return p_stream;
|
|
|
396 }
|
|
|
397
|
|
|
398 #define FB2K_STREAM_READER_OVERLOAD(type) \
|
|
|
399 template<bool isBigEndian> stream_reader_formatter<isBigEndian> & operator>>(stream_reader_formatter<isBigEndian> & stream,type & value)
|
|
|
400
|
|
|
401 #define FB2K_STREAM_WRITER_OVERLOAD(type) \
|
|
|
402 template<bool isBigEndian> stream_writer_formatter<isBigEndian> & operator<<(stream_writer_formatter<isBigEndian> & stream,const type & value)
|
|
|
403
|
|
|
404 FB2K_STREAM_READER_OVERLOAD(GUID) {
|
|
|
405 return stream >> value.Data1 >> value.Data2 >> value.Data3 >> value.Data4;
|
|
|
406 }
|
|
|
407
|
|
|
408 FB2K_STREAM_WRITER_OVERLOAD(GUID) {
|
|
|
409 return stream << value.Data1 << value.Data2 << value.Data3 << value.Data4;
|
|
|
410 }
|
|
|
411
|
|
|
412 FB2K_STREAM_READER_OVERLOAD(pfc::string) {
|
|
|
413 t_uint32 len; stream >> len;
|
|
|
414 value = stream.m_stream.read_string_ex(len,stream.m_abort);
|
|
|
415 return stream;
|
|
|
416 }
|
|
|
417
|
|
|
418 FB2K_STREAM_WRITER_OVERLOAD(pfc::string) {
|
|
|
419 stream << pfc::downcast_guarded<t_uint32>(value.length());
|
|
|
420 stream.write_raw(value.ptr(),value.length());
|
|
|
421 return stream;
|
|
|
422 }
|
|
|
423
|
|
|
424 FB2K_STREAM_READER_OVERLOAD(pfc::string_base) {
|
|
|
425 stream.m_stream.read_string(value, stream.m_abort);
|
|
|
426 return stream;
|
|
|
427 }
|
|
|
428 FB2K_STREAM_WRITER_OVERLOAD(pfc::string_base) {
|
|
|
429 const char * val = value.get_ptr();
|
|
|
430 const t_size len = strlen(val);
|
|
|
431 stream << pfc::downcast_guarded<t_uint32>(len);
|
|
|
432 stream.write_raw(val,len);
|
|
|
433 return stream;
|
|
|
434 }
|
|
|
435
|
|
|
436
|
|
|
437 FB2K_STREAM_WRITER_OVERLOAD(float) {
|
|
|
438 union {
|
|
|
439 float f; t_uint32 i;
|
|
|
440 } u; u.f = value;
|
|
|
441 return stream << u.i;
|
|
|
442 }
|
|
|
443
|
|
|
444 FB2K_STREAM_READER_OVERLOAD(float) {
|
|
|
445 union { float f; t_uint32 i;} u;
|
|
|
446 stream >> u.i; value = u.f;
|
|
|
447 return stream;
|
|
|
448 }
|
|
|
449
|
|
|
450 FB2K_STREAM_WRITER_OVERLOAD(double) {
|
|
|
451 union {
|
|
|
452 double f; t_uint64 i;
|
|
|
453 } u; u.f = value;
|
|
|
454 return stream << u.i;
|
|
|
455 }
|
|
|
456
|
|
|
457 FB2K_STREAM_READER_OVERLOAD(double) {
|
|
|
458 union { double f; t_uint64 i;} u;
|
|
|
459 stream >> u.i; value = u.f;
|
|
|
460 return stream;
|
|
|
461 }
|
|
|
462
|
|
|
463 FB2K_STREAM_WRITER_OVERLOAD(bool) {
|
|
|
464 t_uint8 temp = value ? 1 : 0;
|
|
|
465 return stream << temp;
|
|
|
466 }
|
|
|
467 FB2K_STREAM_READER_OVERLOAD(bool) {
|
|
|
468 t_uint8 temp; stream >> temp; value = temp != 0;
|
|
|
469 return stream;
|
|
|
470 }
|
|
|
471
|
|
|
472 template<bool BE = false>
|
|
|
473 class stream_writer_formatter_simple : public stream_writer_formatter<BE> {
|
|
|
474 public:
|
|
|
475 stream_writer_formatter_simple() : stream_writer_formatter<BE>(_m_stream,fb2k::noAbort), m_buffer(_m_stream.m_buffer) {}
|
|
|
476
|
|
|
477 typedef stream_writer_buffer_simple::t_buffer t_buffer;
|
|
|
478 t_buffer & m_buffer;
|
|
|
479 private:
|
|
|
480 stream_writer_buffer_simple _m_stream;
|
|
|
481 };
|
|
|
482
|
|
|
483 template<bool BE = false>
|
|
|
484 class stream_reader_formatter_simple_ref : public stream_reader_formatter<BE> {
|
|
|
485 public:
|
|
|
486 stream_reader_formatter_simple_ref(const void * source, t_size sourceSize) : stream_reader_formatter<BE>(_m_stream,fb2k::noAbort), _m_stream(source,sourceSize) {}
|
|
|
487 template<typename TSource> stream_reader_formatter_simple_ref(const TSource& source) : stream_reader_formatter<BE>(_m_stream,fb2k::noAbort), _m_stream(source) {}
|
|
|
488 stream_reader_formatter_simple_ref() : stream_reader_formatter<BE>(_m_stream,fb2k::noAbort) {}
|
|
|
489
|
|
|
490 void set_data(const void * source, t_size sourceSize) {_m_stream.set_data(source,sourceSize);}
|
|
|
491 template<typename TSource> void set_data(const TSource & source) {_m_stream.set_data(source);}
|
|
|
492
|
|
|
493 void reset() {_m_stream.reset();}
|
|
|
494 t_size get_remaining() {return _m_stream.get_remaining();}
|
|
|
495
|
|
|
496 const void * get_ptr_() const {return _m_stream.get_ptr_();}
|
|
|
497 private:
|
|
|
498 stream_reader_memblock_ref _m_stream;
|
|
|
499 };
|
|
|
500
|
|
|
501 template<bool BE = false>
|
|
|
502 class stream_reader_formatter_simple : public stream_reader_formatter_simple_ref<BE> {
|
|
|
503 public:
|
|
|
504 stream_reader_formatter_simple() {}
|
|
|
505 stream_reader_formatter_simple(const void * source, t_size sourceSize) {set_data(source,sourceSize);}
|
|
|
506 template<typename TSource> stream_reader_formatter_simple(const TSource & source) {set_data(source);}
|
|
|
507
|
|
|
508 void set_data(const void * source, t_size sourceSize) {
|
|
|
509 m_content.set_data_fromptr(reinterpret_cast<const t_uint8*>(source), sourceSize);
|
|
|
510 onContentChange();
|
|
|
511 }
|
|
|
512 template<typename TSource> void set_data(const TSource & source) {
|
|
|
513 m_content = source;
|
|
|
514 onContentChange();
|
|
|
515 }
|
|
|
516 private:
|
|
|
517 void onContentChange() {
|
|
|
518 stream_reader_formatter_simple_ref<BE>::set_data(m_content);
|
|
|
519 }
|
|
|
520 pfc::array_t<t_uint8> m_content;
|
|
|
521 };
|
|
|
522
|
|
|
523
|
|
|
524
|
|
|
525
|
|
|
526
|
|
|
527
|
|
|
528 template<bool isBigEndian> class _stream_reader_formatter_translator {
|
|
|
529 public:
|
|
|
530 _stream_reader_formatter_translator(stream_reader_formatter<isBigEndian> & stream) : m_stream(stream) {}
|
|
|
531 typedef _stream_reader_formatter_translator<isBigEndian> t_self;
|
|
|
532 template<typename t_what> t_self & operator||(t_what & out) {m_stream >> out; return *this;}
|
|
|
533 private:
|
|
|
534 stream_reader_formatter<isBigEndian> & m_stream;
|
|
|
535 };
|
|
|
536 template<bool isBigEndian> class _stream_writer_formatter_translator {
|
|
|
537 public:
|
|
|
538 _stream_writer_formatter_translator(stream_writer_formatter<isBigEndian> & stream) : m_stream(stream) {}
|
|
|
539 typedef _stream_writer_formatter_translator<isBigEndian> t_self;
|
|
|
540 template<typename t_what> t_self & operator||(const t_what & in) {m_stream << in; return *this;}
|
|
|
541 private:
|
|
|
542 stream_writer_formatter<isBigEndian> & m_stream;
|
|
|
543 };
|
|
|
544
|
|
|
545 #define FB2K_STREAM_RECORD_OVERLOAD(type, code) \
|
|
|
546 FB2K_STREAM_READER_OVERLOAD(type) { \
|
|
|
547 _stream_reader_formatter_translator<isBigEndian> streamEx(stream); \
|
|
|
548 streamEx || code; \
|
|
|
549 return stream; \
|
|
|
550 } \
|
|
|
551 FB2K_STREAM_WRITER_OVERLOAD(type) { \
|
|
|
552 _stream_writer_formatter_translator<isBigEndian> streamEx(stream); \
|
|
|
553 streamEx || code; \
|
|
|
554 return stream; \
|
|
|
555 }
|
|
|
556
|
|
|
557
|
|
|
558
|
|
|
559
|
|
|
560 #define FB2K_RETRY_ON_EXCEPTION(OP, ABORT, TIMEOUT, EXCEPTION) \
|
|
|
561 { \
|
|
|
562 pfc::lores_timer timer; timer.start(); \
|
|
|
563 for(;;) { \
|
|
|
564 try { {OP;} break; } \
|
|
|
565 catch(const EXCEPTION &) { if (timer.query() > TIMEOUT) throw;} \
|
|
|
566 ABORT.sleep(0.05); \
|
|
|
567 } \
|
|
|
568 }
|
|
|
569
|
|
|
570 #define FB2K_RETRY_ON_EXCEPTION2(OP, ABORT, TIMEOUT, EXCEPTION1, EXCEPTION2) \
|
|
|
571 { \
|
|
|
572 pfc::lores_timer timer; timer.start(); \
|
|
|
573 for(;;) { \
|
|
|
574 try { {OP;} break; } \
|
|
|
575 catch(const EXCEPTION1 &) { if (timer.query() > TIMEOUT) throw;} \
|
|
|
576 catch(const EXCEPTION2 &) { if (timer.query() > TIMEOUT) throw;} \
|
|
|
577 ABORT.sleep(0.05); \
|
|
|
578 } \
|
|
|
579 }
|
|
|
580
|
|
|
581 #define FB2K_RETRY_ON_EXCEPTION3(OP, ABORT, TIMEOUT, EXCEPTION1, EXCEPTION2, EXCEPTION3) \
|
|
|
582 { \
|
|
|
583 pfc::lores_timer timer; timer.start(); \
|
|
|
584 for(;;) { \
|
|
|
585 try { {OP;} break; } \
|
|
|
586 catch(const EXCEPTION1 &) { if (timer.query() > TIMEOUT) throw;} \
|
|
|
587 catch(const EXCEPTION2 &) { if (timer.query() > TIMEOUT) throw;} \
|
|
|
588 catch(const EXCEPTION3 &) { if (timer.query() > TIMEOUT) throw;} \
|
|
|
589 ABORT.sleep(0.05); \
|
|
|
590 } \
|
|
|
591 }
|
|
|
592
|
|
|
593 #define FB2K_RETRY_ON_SHARING_VIOLATION(OP, ABORT, TIMEOUT) FB2K_RETRY_ON_EXCEPTION(OP, ABORT, TIMEOUT, exception_io_sharing_violation)
|
|
|
594
|
|
|
595 // **** WINDOWS SUCKS ****
|
|
|
596 // 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
|
|
|
597 #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)
|
|
|
598
|
|
|
599 class fileRestorePositionScope {
|
|
|
600 public:
|
|
|
601 fileRestorePositionScope(file::ptr f, abort_callback & a) : m_file(f), m_abort(a) {
|
|
|
602 m_offset = f->get_position(a);
|
|
|
603 }
|
|
|
604 ~fileRestorePositionScope() {
|
|
|
605 try {
|
|
|
606 if (!m_abort.is_aborting()) m_file->seek(m_offset, m_abort);
|
|
|
607 } catch(...) {}
|
|
|
608 }
|
|
|
609 private:
|
|
|
610 file::ptr m_file;
|
|
|
611 t_filesize m_offset;
|
|
|
612 abort_callback & m_abort;
|
|
|
613 };
|
|
|
614
|
|
|
615
|
|
|
616 //! Debug self-test function for testing a file object implementation, performs various behavior validity checks, random access etc. Output goes to fb2k console.
|
|
|
617 //! Returns true on success, false on failure (buggy file object implementation).
|
|
|
618 bool fb2kFileSelfTest(file::ptr f, abort_callback & aborter);
|