+#ifndef CXR_IO_H
+#define CXR_IO_H
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "cxr_types.h"
+
+/* Read binary or text assets in full from file */
+static void *cxr_asset_read_s( const char *path, i64 *size );
+static void *cxr_asset_read( const char *path );
+static char *cxr_textasset_read_s( const char *path, i64 *size );
+static char *cxr_textasset_read( const char *name );
+
+static i64 cxr_file_size( FILE *fileptr );
+
+/* Path utilities */
+/* Returns pointer to the extension in path */
+static char *cxr_findext( char *path, char const delim );
+static char *cxr_findsep( char *path );
+
+static void cxr_stripext( char *path );
+static int cxr_path_is_abs( char const *path );
+static char *cxr_filename( char *path );
+
+/* Remove one level (nop if can't) eg: /home/harry/test.file -> /home/harry/ */
+static void cxr_downlvl( char *path );
+
+#ifdef _WIN32
+ #define CXR_FOLDER_CHAR '\\'
+#else
+ #define CXR_FOLDER_CHAR '/'
+#endif
+
+static i64 cxr_file_size( FILE *fileptr )
+{
+ fseek( fileptr, 0, SEEK_END );
+ i64 fsize = ftell( fileptr );
+ fseek( fileptr, 0, SEEK_SET );
+
+ return fsize;
+}
+
+static void *fs_disk_open_read( const char *path, int reserve_end, i64 *size )
+{
+ FILE *f = fopen( path, "rb" );
+ if( f )
+ {
+ i64 fsize = cxr_file_size( f );
+ void *buf = malloc( fsize + reserve_end );
+
+ if( buf )
+ {
+ if( fread( buf, 1, fsize, f ) != fsize )
+ {
+ free( buf );
+ buf = NULL;
+ }
+ }
+
+ *size = fsize;
+
+ fclose( f );
+ return buf;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+static char *fs_disk_load_text( const char *path, i64 *size )
+{
+ char *buf;
+ i64 fsize;
+
+ if( (buf = fs_disk_open_read( path, 1, &fsize )) )
+ {
+ buf[ fsize ] = 0x00;
+ *size = fsize +1;
+
+ return buf;
+ }
+
+ return NULL;
+}
+
+static void *cxr_asset_read_s( const char *path, i64 *size )
+{
+ return fs_disk_open_read( path, 0, size );
+}
+
+static void *cxr_asset_read( const char *path )
+{
+ i64 size;
+ return fs_disk_open_read( path, 0, &size );
+}
+
+static char *cxr_textasset_read_s( const char *path, i64 *size )
+{
+ return fs_disk_load_text( path, size );
+}
+
+static char *cxr_textasset_read( const char *name )
+{
+ i64 size;
+ return fs_disk_load_text( name, &size );
+}
+
+static char *cxr_findext( char *path, char const delim )
+{
+ char *c, *ptr;
+
+ c = path;
+ ptr = NULL;
+
+ while( *c )
+ {
+ if( *c == delim )
+ {
+ ptr = c + 1;
+ }
+
+ c ++;
+ }
+
+ return ptr;
+}
+
+static char *cxr_findsep( char *path )
+{
+ char *c, *ptr;
+
+ c = path;
+ ptr = NULL;
+
+ while( *c )
+ {
+ if( *c == '/' || *c == '\\' )
+ {
+ ptr = c + 1;
+ }
+
+ c ++;
+ }
+
+ return ptr;
+}
+
+static void cxr_stripext( char *path )
+{
+ char *point, *start;
+
+ // Skip folders
+ if( !(start = cxr_findsep( path )) )
+ {
+ start = path;
+ }
+
+ if( (point = cxr_findext( start, '.' )) )
+ {
+ if( point > path )
+ {
+ *(point-1) = 0x00;
+ }
+ }
+}
+
+static void cxr_downlvl( char *path )
+{
+ char *start_name, *c;
+
+ c = path;
+ while( *c )
+ c ++;
+ int len = c - path;
+
+ if( len )
+ path[ len -1 ] = 0x00;
+
+ if( (start_name = cxr_findsep( path ) ))
+ *start_name = 0x00;
+ else
+ path[0] = 0x00;
+}
+
+static char *cxr_filename( char *path )
+{
+ char *base_name;
+ if( (base_name = cxr_findsep( path ) ))
+ return base_name;
+
+ return path;
+}
+
+static int cxr_path_is_abs( char const *path )
+{
+#ifdef _WIN32
+ if( strlen( path ) < 2 ) return 0;
+ return path[1] == ':';
+#else
+ if( strlen( path ) < 1 ) return 0;
+ return path[0] == '/';
+#endif
+}
+
+static char *cxr_str_clone( char const *str, int extra )
+{
+ char *newstr = malloc(strlen(str)+1+extra);
+ strcpy( newstr, str );
+
+ return newstr;
+}
+
+#endif /* CXR_IO_H */