maug
Quick and dirty C mini-augmentation library.
mfile.h
Go to the documentation of this file.
1
2#ifndef MFILE_H
3#define MFILE_H
4
5#if !defined( DEBUG_THRESHOLD )
6# define DEBUG_THRESHOLD 1
7#endif /* !DEBUG_THRESHOLD */
8
9#ifndef UPRINTF_BUFFER_SZ_MAX
10# define UPRINTF_BUFFER_SZ_MAX 1024
11#endif /* !UPRINTF_BUFFER_SZ_MAX */
12
13#if !defined( MAUG_NO_RETRO ) && !defined( DOCUMENTATION )
15 uint8_t flags, const char* title, const char* format, ... );
16#endif /* !MAUG_NO_RETRO && !DOCUMENTATION */
17
18#if !defined( MFILE_MMAP ) && \
19 !defined( RETROFLAT_API_WINCE ) && \
20 !defined( RETROFLAT_API_PALM ) && \
21 !defined( RETROFLAT_API_PSN )
22# include <sys/stat.h>
23#endif /* !MFILE_MMAP */
24
25/* TODO: async file_open() call that kicks off download to mem buffer that can
26 * be checked with looped check function.
27 */
28
39#ifndef MAUG_PATH_SZ_MAX
41# define MAUG_PATH_SZ_MAX 256
42#endif /* !MAUG_PATH_SZ_MAX */
43
56#define MFILE_CADDY_TYPE_FILE 0x01
57
61#define MFILE_CADDY_TYPE_MEM_BUFFER 0x80
62 /* maug_mfile_types */
64
69#define MFILE_FLAG_READ_ONLY 0x01
70
77#define MFILE_FLAG_HANDLE_LOCKED 0x02
78
98#define MFILE_READ_FLAG_LSBF 0x01
99
105#define MFILE_READ_FLAG_MSBF 0x01
106
107#define MFILE_ASSIGN_FLAG_TRIM_EXT 0x01
108
109#ifndef MFILE_READ_TRACE_LVL
110# define MFILE_READ_TRACE_LVL 0
111#endif /* !MFILE_READ_TRACE_LVL */
112
113#ifndef MFILE_WRITE_TRACE_LVL
114# define MFILE_WRITE_TRACE_LVL 0
115#endif /* !MFILE_WRITE_TRACE_LVL */
116
117#ifndef MFILE_SEEK_TRACE_LVL
118# define MFILE_SEEK_TRACE_LVL 0
119#endif /* !MFILE_SEEK_TRACE_LVL */
120
121#ifndef MFILE_CONTENTS_TRACE_LVL
122# define MFILE_CONTENTS_TRACE_LVL 0
123#endif /* !MFILE_CONTENTS_TRACE_LVL */
124
142
146#define mfile_cmp_path( a, b ) strncmp( a, b, MAUG_PATH_SZ_MAX )
147 /* maug_retroflt_assets */
149
150struct MFILE_CADDY;
151typedef struct MFILE_CADDY mfile_t;
152
153typedef off_t (*mfile_cursor_t)( struct MFILE_CADDY* p_file );
154typedef off_t (*mfile_has_bytes_t)( struct MFILE_CADDY* p_file );
155typedef MERROR_RETVAL (*mfile_read_byte_t)(
156 struct MFILE_CADDY* p_file, uint8_t* buf );
157typedef MERROR_RETVAL (*mfile_read_block_t)(
158 struct MFILE_CADDY* p_file, uint8_t* buf, size_t buf_sz );
159typedef MERROR_RETVAL (*mfile_seek_t)( struct MFILE_CADDY* p_file, off_t pos );
160typedef MERROR_RETVAL (*mfile_read_int_t)(
161 struct MFILE_CADDY* p_file, uint8_t* buf, size_t buf_sz, uint8_t flags );
162typedef MERROR_RETVAL (*mfile_read_line_t)(
163 struct MFILE_CADDY* p_file, char* buf, off_t buf_sz, uint8_t flags );
164typedef MERROR_RETVAL (*mfile_printf_t)(
165 struct MFILE_CADDY* p_file, uint8_t flags, const char* fmt, ... );
166typedef MERROR_RETVAL (*mfile_write_block_t)(
167 struct MFILE_CADDY* p_f, const uint8_t* buf, size_t buf_sz );
168
169off_t mfile_mem_cursor( struct MFILE_CADDY* p_file );
170off_t mfile_mem_has_bytes( struct MFILE_CADDY* p_file );
171MERROR_RETVAL mfile_mem_read_byte( struct MFILE_CADDY* p_file, uint8_t* buf );
172MERROR_RETVAL mfile_mem_read_block(
173 struct MFILE_CADDY* p_file, uint8_t* buf, size_t buf_sz );
174MERROR_RETVAL mfile_mem_seek( struct MFILE_CADDY* p_file, off_t pos );
175MERROR_RETVAL mfile_mem_read_line(
176 struct MFILE_CADDY* p_f, char* buffer, off_t buffer_sz, uint8_t flags );
177MERROR_RETVAL mfile_mem_vprintf(
178 mfile_t* p_file, uint8_t flags, const char* fmt, va_list args );
179
188 struct MFILE_CADDY* p_f, const uint8_t* buf, size_t buf_sz );
189
190MERROR_RETVAL mfile_plt_init();
191
200 struct MFILE_CADDY* p_file, uint8_t flags, const char* fmt, va_list args );
201
202/* Load the platform-specific file API. */
203#include <mrapifil.h>
204#include <mrapilog.h>
205
208 uint8_t type;
210 union MFILE_HANDLE h;
211 off_t last_read;
215 uint8_t* mem_buffer;
216 uint8_t flags;
218 off_t sz;
219 maug_path filename;
220 mfile_has_bytes_t has_bytes;
221 mfile_cursor_t cursor;
222 mfile_read_byte_t read_byte;
223 mfile_read_block_t read_block;
224 mfile_seek_t seek;
225 mfile_read_int_t read_int;
226 mfile_read_line_t read_line;
227 mfile_printf_t printf;
228 mfile_vprintf_t vprintf;
229 mfile_write_block_t write_block;
230};
231
232typedef struct MFILE_CADDY mfile_t;
233
244 maug_path tgt, const maug_path src, uint8_t flags );
245
248off_t mfile_file_has_bytes( struct MFILE_CADDY* p_file );
249
250MERROR_RETVAL mfile_file_read_byte( struct MFILE_CADDY* p_file, uint8_t* buf );
251
252MERROR_RETVAL mfile_file_read_block(
253 struct MFILE_CADDY* p_file, uint8_t* buf, size_t buf_sz );
254
255MERROR_RETVAL mfile_file_read_int(
256 struct MFILE_CADDY* p_f, uint8_t* buf, size_t buf_sz, uint8_t flags );
257
258MERROR_RETVAL mfile_file_seek( struct MFILE_CADDY* p_file, off_t pos );
259
260MERROR_RETVAL mfile_file_read_line(
261 struct MFILE_CADDY* p_f, char* buffer, off_t buffer_sz, uint8_t flags );
262
263MERROR_RETVAL mfile_file_printf(
264 struct MFILE_CADDY* p_f, uint8_t flags, const char* fmt, ... );
265
266MERROR_RETVAL mfile_file_write_block(
267 struct MFILE_CADDY* p_f, const uint8_t* buf, size_t buf_sz );
268
269MERROR_RETVAL mfile_file_vprintf(
270 struct MFILE_CADDY* p_f, uint8_t flags, const char* fmt, va_list args );
271
272#define mfile_get_sz( p_file ) ((p_file)->sz)
273
278 MAUG_MHANDLE, void* ptr, off_t, mfile_t* p_file );
279
285 const maug_path filename, mfile_t* p_file );
286
287MERROR_RETVAL mfile_open_write(
288 const maug_path filename, mfile_t* p_file );
289
293void mfile_close( mfile_t* p_file );
294
295#ifdef MFILE_C
296
297#include <mrapifil.h>
298#include <mrapilog.h>
299
300off_t mfile_file_has_bytes( struct MFILE_CADDY* p_file ) {
301 size_t cursor = 0;
302 cursor = p_file->cursor( p_file );
303 if( p_file->sz > cursor ) {
304#if MFILE_READ_TRACE_LVL > 0
305 debug_printf( MFILE_READ_TRACE_LVL,
306 "file has " OFF_T_FMT " bytes left...",
307 p_file->sz - cursor );
308#endif /* MFILE_READ_TRACE_LVL */
309 return p_file->sz - cursor;
310 } else {
311#if MFILE_READ_TRACE_LVL > 0
312 /* TODO: Improved error message/handling. */
313 debug_printf( MFILE_READ_TRACE_LVL, "file has error bytes left!" );
314#endif /* MFILE_READ_TRACE_LVL */
315 return 0;
316 }
317}
318
319/* === */
320
322 maug_path tgt, const maug_path src, uint8_t flags
323) {
324 MERROR_RETVAL retval = MERROR_OK;
325 char* ext_ptr = NULL;
326
327 maug_mzero( tgt, MAUG_PATH_SZ_MAX );
328 maug_snprintf( tgt, MAUG_PATH_SZ_MAX - 1, "%s", src );
329
330 if( MFILE_ASSIGN_FLAG_TRIM_EXT == (MFILE_ASSIGN_FLAG_TRIM_EXT & flags) ) {
331 ext_ptr = maug_strrchr( tgt, '.' );
332 if( NULL != ext_ptr ) {
333 *ext_ptr = '\0';
334 }
335 }
336
337 return retval;
338}
339
340/* === */
341
342MERROR_RETVAL mfile_file_read_int(
343 struct MFILE_CADDY* p_file, uint8_t* buf, size_t buf_sz, uint8_t flags
344) {
345 MERROR_RETVAL retval = MERROR_OK;
346
347 if(
348#ifdef MAUG_LSBF
350#elif defined( MAUG_MSBF )
352#endif
353 ) {
354 debug_printf( MFILE_READ_TRACE_LVL, "reading integer forward" );
355 /* Shrink the buffer moving right and read into it. */
356 retval = p_file->read_block( p_file, buf, buf_sz );
357
358 } else {
359 debug_printf( MFILE_READ_TRACE_LVL, "reading integer reversed" );
360 /* Move to the end of the output buffer and read backwards. */
361 while( 0 < buf_sz ) {
362 retval = p_file->read_byte( p_file, (buf + (buf_sz - 1)) );
363 maug_cleanup_if_not_ok();
364 buf_sz--;
365 }
366 }
367
368cleanup:
369
370 return retval;
371}
372
373/* === */
374
375MERROR_RETVAL mfile_file_printf(
376 struct MFILE_CADDY* p_file, uint8_t flags, const char* fmt, ...
377) {
378 MERROR_RETVAL retval = MERROR_OK;
379 va_list vargs;
380
381 va_start( vargs, fmt );
382 retval = p_file->vprintf( p_file, flags, fmt, vargs );
383 va_end( vargs );
384
385 return retval;
386}
387
388/* === */
389
390off_t mfile_mem_cursor( struct MFILE_CADDY* p_file ) {
391 return p_file->mem_cursor;
392}
393
394/* === */
395
396off_t mfile_mem_has_bytes( struct MFILE_CADDY* p_file ) {
397 return p_file->sz - p_file->mem_cursor;
398}
399
400/* === */
401
402static MERROR_RETVAL mfile_mem_lock( struct MFILE_CADDY* p_f ) {
403 MERROR_RETVAL retval = MERROR_OK;
404
405 assert(
407
408 assert( MFILE_CADDY_TYPE_MEM_BUFFER == p_f->type );
409
410 /* Only lock if this buffer uses a handle and not a pointer. */
411 if( (MAUG_MHANDLE)NULL != p_f->h.mem ) {
412 assert( NULL == p_f->mem_buffer );
413 maug_mlock( p_f->h.mem, p_f->mem_buffer );
414 p_f->flags |= MFILE_FLAG_HANDLE_LOCKED;
415 }
416
417 return retval;
418}
419
420/* === */
421
422static void mfile_mem_release( struct MFILE_CADDY* p_f ) {
423
424 assert( MFILE_CADDY_TYPE_MEM_BUFFER == p_f->type );
425
426 if( MFILE_FLAG_HANDLE_LOCKED == (MFILE_FLAG_HANDLE_LOCKED & p_f->flags) ) {
427 maug_munlock( p_f->h.mem, p_f->mem_buffer );
428 p_f->flags &= ~MFILE_FLAG_HANDLE_LOCKED;
429 }
430}
431
432/* === */
433
434MERROR_RETVAL mfile_mem_read_byte( struct MFILE_CADDY* p_file, uint8_t* buf ) {
435 return p_file->read_block( p_file, buf, 1 );
436}
437
438/* === */
439
440MERROR_RETVAL mfile_mem_read_block(
441 struct MFILE_CADDY* p_file, uint8_t* buf, size_t buf_sz
442) {
443 MERROR_RETVAL retval = MERROR_OK;
444
445 if( p_file->mem_cursor >= p_file->sz ) {
446 return MERROR_FILE;
447 }
448
449 retval = mfile_mem_lock( p_file );
450 maug_cleanup_if_not_ok();
451
452 while( 0 < buf_sz-- ) {
453 *(buf++) = p_file->mem_buffer[p_file->mem_cursor++];
454 }
455
456cleanup:
457
458 mfile_mem_release( p_file );
459
460 return retval;
461}
462
463/* === */
464
465MERROR_RETVAL mfile_mem_seek( struct MFILE_CADDY* p_file, off_t pos ) {
466 MERROR_RETVAL retval = MERROR_OK;
467
468 assert( MFILE_CADDY_TYPE_MEM_BUFFER == p_file->type );
469
470 p_file->mem_cursor = pos;
471
472 debug_printf( MFILE_SEEK_TRACE_LVL,
473 "seeking memory buffer to position " OFF_T_FMT " (" OFF_T_FMT ")",
474 pos, p_file->mem_cursor );
475
476 return retval;
477}
478
479/* === */
480
481MERROR_RETVAL mfile_mem_read_line(
482 struct MFILE_CADDY* p_f, char* buffer, off_t buffer_sz, uint8_t flags
483) {
484 MERROR_RETVAL retval = MERROR_OK;
485 off_t i = 0;
486
487 assert( MFILE_CADDY_TYPE_MEM_BUFFER == p_f->type );
488
489 /* Check for no bytes at the start, as the loop below won't trigger if so! */
490 if( !p_f->has_bytes( p_f ) ) {
491 error_printf( "file %s out of bytes!", p_f->filename );
492 retval = MERROR_FILE;
493 goto cleanup;
494 }
495
496 while( i < buffer_sz - 1 && p_f->has_bytes( p_f ) ) {
497 /* Check for potential overflow. */
498 if( i + 1 >= buffer_sz ) {
499 error_printf( "overflow reading string from file %s!", p_f->filename );
500 retval = MERROR_FILE;
501 break;
502 }
503
504 retval = p_f->read_int( p_f, (uint8_t*)&(buffer[i]), 1, 0 );
505 maug_cleanup_if_not_ok();
506 if( '\n' == buffer[i] ) {
507 /* Break on newline and overwrite it below. */
508 break;
509 }
510 i++;
511 }
512
513 assert( i < buffer_sz ); /* while() above stops before buffer_sz! */
514
515 /* Append a null terminator. */
516 buffer[i] = '\0';
517
518cleanup:
519
520 return retval;
521}
522
523/* === */
524
525MERROR_RETVAL mfile_mem_vprintf(
526 mfile_t* p_file, uint8_t flags, const char* fmt, va_list args
527) {
528 MERROR_RETVAL retval = MERROR_OK;
529
530 if( MFILE_FLAG_READ_ONLY == (MFILE_FLAG_READ_ONLY & p_file->flags) ) {
531 return MERROR_FILE;
532 }
533
534 /* TODO: Enable writing to memory buffer. */
535 error_printf( "writing to memory buffer not currently supported!" );
536
537 /* TODO: Expand buffer and reflect this in sz if writing beyond the end
538 * of the buffer.
539 */
540
541 return retval;
542}
543
544/* === */
545
547 struct MFILE_CADDY* p_f, const uint8_t* buf, size_t buf_sz
548) {
549 MERROR_RETVAL retval = MERROR_OK;
550 ssize_t mv_sz = 0;
551
552 if( 0 == buf_sz ) {
553 /* Short-circuit empty writes. */
554 return MERROR_OK;
555 }
556
557 if( MFILE_FLAG_READ_ONLY == (MFILE_FLAG_READ_ONLY & p_f->flags) ) {
558 return MERROR_FILE;
559 }
560
561 retval = mfile_mem_lock( p_f );
562 maug_cleanup_if_not_ok();
563
564#if MFILE_WRITE_TRACE_LVL > 0
565 debug_printf( MFILE_WRITE_TRACE_LVL, "p: %p, sz: %u, cur: %u, buf_sz: %u\n",
566 p_f->mem_buffer, p_f->sz, p_f->mem_cursor, buf_sz );
567#endif /* MFILE_WRITE_TRACE_LVL */
568
569 mv_sz = (p_f->sz - (p_f->mem_cursor + buf_sz));
570 if( 0 < mv_sz ) {
571 memmove(
572 &(p_f->mem_buffer[p_f->mem_cursor + buf_sz]),
573 &(p_f->mem_buffer[p_f->mem_cursor]),
574 mv_sz );
575 }
576
577 memcpy( &(p_f->mem_buffer[p_f->mem_cursor]), buf, buf_sz );
578 p_f->mem_cursor += buf_sz;
579
580 /* TODO: Expand buffer and reflect this in sz if writing beyond the end
581 * of the buffer.
582 */
583
584cleanup:
585
586 mfile_mem_release( p_f );
587
588 return retval;
589}
590
591/* === */
592
594 MAUG_MHANDLE handle, void* ptr, off_t handle_sz, mfile_t* p_file
595) {
596 MERROR_RETVAL retval = MERROR_OK;
597
598 debug_printf( MFILE_SEEK_TRACE_LVL,
599 "locking handle %p as file %p (" OFF_T_FMT " bytes)...",
600 handle, p_file, handle_sz );
601
602 maug_mzero( p_file, sizeof( struct MFILE_CADDY ) );
603
604 /* Determine if this is based on a handle or a pointer. */
605 if( (MAUG_MHANDLE)NULL == handle && NULL != ptr ) {
606 p_file->mem_buffer = ptr;
607 } else if( (MAUG_MHANDLE)NULL != handle && NULL == ptr ) {
608 p_file->h.mem = handle;
609 /* maug_mlock( handle, p_file->mem_buffer ); */
610 } else {
611 error_printf( "must specify handle or pointer!" );
612 retval = MERROR_FILE;
613 goto cleanup;
614 }
615
617
618 p_file->has_bytes = mfile_mem_has_bytes;
619 p_file->cursor = mfile_mem_cursor;
620 p_file->read_byte = mfile_mem_read_byte;
621 p_file->read_block = mfile_mem_read_block;
622 p_file->read_int = mfile_file_read_int;
623 p_file->seek = mfile_mem_seek;
624 p_file->read_line = mfile_mem_read_line;
625 p_file->write_block = mfile_mem_write_block;
626
627 p_file->sz = handle_sz;
628
629cleanup:
630
631 return retval;
632}
633
634/* === */
635
637 const maug_path filename, mfile_t* p_file
638) {
639 MERROR_RETVAL retval = MERROR_OK;
640
641 /* Call the platform-specific actual file opener from mrapifil.h. */
642 retval = mfile_plt_open_read( filename, p_file );
643
644 if( MERROR_OK == retval ) {
645 /* Store filename. */
646 retval = mfile_assign_path( p_file->filename, filename, 0 );
647 }
648
649 return retval;
650}
651
652/* === */
653
654MERROR_RETVAL mfile_open_write(
655 const maug_path filename, mfile_t* p_file
656) {
657 MERROR_RETVAL retval = MERROR_OK;
658
659 retval = mfile_plt_open_write( filename, p_file );
660
661 if( MERROR_OK == retval ) {
662 /* Store filename. */
663 retval = mfile_assign_path( p_file->filename, filename, 0 );
664 }
665
666 return retval;
667}
668
669/* === */
670
671void mfile_close( mfile_t* p_file ) {
672# if MFILE_SEEK_TRACE_LVL > 0
673 debug_printf( MFILE_SEEK_TRACE_LVL, "closing file..." );
674# endif /* MFILE_SEEK_TRACE_LVL */
675# ifdef MFILE_MMAP
676 munmap( bytes_ptr_h, bytes_sz );
677# else
678 /* maug_mfree( bytes_ptr_h ); */
679 switch( p_file->type ) {
680 case 0:
681 /* Do nothing silently. */
682 break;
683
685 mfile_plt_close( p_file );
686 p_file->type = 0;
687 break;
688
690 if( NULL != p_file->mem_buffer ) {
691 maug_munlock( p_file->h.mem, p_file->mem_buffer );
692 debug_printf( MFILE_SEEK_TRACE_LVL,
693 "unlocked handle %p from file %p...",
694 p_file->h.mem, p_file );
695 p_file->type = 0;
696 }
697 break;
698
699 default:
700 error_printf( "unknown file type: %d", (p_file)->type );
701 break;
702 }
703# endif /* MFILE_MMAP */
704}
705
706#endif /* MFILE_C */
707 /* maug_mfile */
709
710#endif /* !MFILE_H */
711
uint16_t MERROR_RETVAL
Return type indicating function returns a value from this list.
Definition: merror.h:28
#define MFILE_CADDY_TYPE_FILE
A standard UNIX file.
Definition: mfile.h:56
#define MFILE_CADDY_TYPE_MEM_BUFFER
An array of bytes in memory abstracted through this library.
Definition: mfile.h:61
MERROR_RETVAL mfile_open_read(const maug_path filename, mfile_t *p_file)
Open a file and read it into memory or memory-map it.
MERROR_RETVAL mfile_lock_buffer(MAUG_MHANDLE, void *ptr, off_t, mfile_t *p_file)
Lock a buffer and assign it to an ::mfile_t to read/write.
#define MAUG_PATH_SZ_MAX
Maximum size allocated for asset paths.
Definition: mfile.h:41
#define MFILE_READ_FLAG_MSBF
Flag for mfile_read_int_t() indicating integer should always be read most significant byte first.
Definition: mfile.h:105
MERROR_RETVAL(* mfile_vprintf_t)(struct MFILE_CADDY *p_file, uint8_t flags, const char *fmt, va_list args)
Callback to printf the given format string, replacing tokens from the providied pre-initialized list ...
Definition: mfile.h:199
#define MFILE_READ_FLAG_LSBF
Flag for mfile_read_int_t() indicating integer should always be read least significant byte first.
Definition: mfile.h:98
MERROR_RETVAL mfile_mem_write_block(struct MFILE_CADDY *p_f, const uint8_t *buf, size_t buf_sz)
Insert provided buffer into the given file.
void mfile_close(mfile_t *p_file)
Close a file opened with mfile_open_read().
MERROR_RETVAL mfile_assign_path(maug_path tgt, const maug_path src, uint8_t flags)
Copy a maug_path from one place to another, safely observing character limits, etc.
char maug_path[MAUG_PATH_SZ_MAX]
Path/name used to load an asset from disk or access other files.
Definition: mfile.h:141
void retroflat_message(uint8_t flags, const char *title, const char *format,...)
Display a message in a dialog box and/or on stderr.
Definition: mfile.h:206
off_t mem_cursor
Current position if its type is MFILE_CADDY_TYPE_MEM_BUFFER.
Definition: mfile.h:213
#define MFILE_FLAG_HANDLE_LOCKED
Flag for MFILE_CADDY::flags indicating subsequent internal unlocks should unlock the handle back to i...
Definition: mfile.h:77
uint8_t type
The RetroFile Types flag describing this file.
Definition: mfile.h:208
union MFILE_HANDLE h
The physical handle or pointer to access the file by.
Definition: mfile.h:210
uint8_t * mem_buffer
Locked pointer for MFILE_HANDLE::mem.
Definition: mfile.h:215
#define MFILE_FLAG_READ_ONLY
Flag for MFILE_CADDY::flags indicating this file is read-only.
Definition: mfile.h:69
off_t sz
Size of the current file/buffer in bytes.
Definition: mfile.h:218