1 /*
2  * libopenmpt_stream_callbacks_buffer.h
3  * ------------------------------------
4  * Purpose: libopenmpt public c interface
5  * Notes  : (currently none)
6  * Authors: OpenMPT Devs
7  * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
8  */
9 
10 #ifndef LIBOPENMPT_STREAM_CALLBACKS_BUFFER_H
11 #define LIBOPENMPT_STREAM_CALLBACKS_BUFFER_H
12 
13 #include "libopenmpt.h"
14 
15 /* The use of this header requires:
16 
17 #include <libopenmpt/libopenmpt.h>
18 #if defined( LIBOPENMPT_STREAM_CALLBACKS_BUFFER )
19 #include <libopenmpt/libopenmpt_stream_callbacks_buffer.h>
20 #else
21 #error "libopenmpt too old."
22 #endif
23 
24 */
25 
26 #include <stdint.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 /*! \addtogroup libopenmpt_c
31  * @{
32  */
33 
34 #ifdef __cplusplus
35 extern "C" {
36 #endif
37 
38 typedef struct openmpt_stream_buffer {
39 	const void * file_data; /* or prefix data IFF prefix_size < file_size */
40 	int64_t file_size;
41 	int64_t file_pos;
42 	int64_t prefix_size;
43 	int overflow;
44 } openmpt_stream_buffer;
45 
openmpt_stream_buffer_read_func(void * stream,void * dst,size_t bytes)46 static size_t openmpt_stream_buffer_read_func( void * stream, void * dst, size_t bytes ) {
47 	openmpt_stream_buffer * s = (openmpt_stream_buffer*)stream;
48 	int64_t offset = 0;
49 	int64_t begpos = 0;
50 	int64_t endpos = 0;
51 	size_t valid_bytes = 0;
52 	if ( !s ) {
53 		return 0;
54 	}
55 	offset = bytes;
56 	begpos = s->file_pos;
57 	endpos = s->file_pos;
58 	valid_bytes = 0;
59 	endpos = (uint64_t)endpos + (uint64_t)offset;
60 	if ( ( offset > 0 ) && !( (uint64_t)endpos > (uint64_t)begpos ) ) {
61 		/* integer wrapped */
62 		return 0;
63 	}
64 	if ( bytes == 0 ) {
65 		return 0;
66 	}
67 	if ( begpos >= s->file_size ) {
68 		return 0;
69 	}
70 	if ( endpos > s->file_size ) {
71 		/* clip to eof */
72 		bytes = bytes - (size_t)( endpos - s->file_size );
73 		endpos = endpos - ( endpos - s->file_size );
74 	}
75 	memset( dst, 0, bytes );
76 	if ( begpos >= s->prefix_size ) {
77 		s->overflow = 1;
78 		valid_bytes = 0;
79 	} else if ( endpos > s->prefix_size ) {
80 		s->overflow = 1;
81 		valid_bytes = bytes - (size_t)( endpos - s->prefix_size );
82 	} else {
83 		valid_bytes = bytes;
84 	}
85 	memcpy( dst, (const char*)s->file_data + s->file_pos, valid_bytes );
86 	s->file_pos = s->file_pos + bytes;
87 	return bytes;
88 }
89 
openmpt_stream_buffer_seek_func(void * stream,int64_t offset,int whence)90 static int openmpt_stream_buffer_seek_func( void * stream, int64_t offset, int whence ) {
91 	openmpt_stream_buffer * s = (openmpt_stream_buffer*)stream;
92 	int result = -1;
93 	if ( !s ) {
94 		return -1;
95 	}
96 	switch ( whence ) {
97 		case OPENMPT_STREAM_SEEK_SET:
98 			if ( offset < 0 ) {
99 				return -1;
100 			}
101 			if ( offset > s->file_size ) {
102 				return -1;
103 			}
104 			s->file_pos = offset;
105 			result = 0;
106 			break;
107 		case OPENMPT_STREAM_SEEK_CUR:
108 			do {
109 				int64_t oldpos = s->file_pos;
110 				int64_t pos = s->file_pos;
111 				pos = (uint64_t)pos + (uint64_t)offset;
112 				if ( ( offset > 0 ) && !( (uint64_t)pos > (uint64_t)oldpos ) ) {
113 					/* integer wrapped */
114 					return -1;
115 				}
116 				if ( ( offset < 0 ) && !( (uint64_t)pos < (uint64_t)oldpos ) ) {
117 					/* integer wrapped */
118 					return -1;
119 				}
120 				s->file_pos = pos;
121 			} while(0);
122 			result = 0;
123 			break;
124 		case OPENMPT_STREAM_SEEK_END:
125 			if ( offset > 0 ) {
126 				return -1;
127 			}
128 			do {
129 				int64_t oldpos = s->file_pos;
130 				int64_t pos = s->file_pos;
131 				pos = s->file_size;
132 				pos = (uint64_t)pos + (uint64_t)offset;
133 				if ( ( offset < 0 ) && !( (uint64_t)pos < (uint64_t)oldpos ) ) {
134 					/* integer wrapped */
135 					return -1;
136 				}
137 				s->file_pos = pos;
138 			} while(0);
139 			result = 0;
140 			break;
141 	}
142 	return result;
143 }
144 
openmpt_stream_buffer_tell_func(void * stream)145 static int64_t openmpt_stream_buffer_tell_func( void * stream ) {
146 	openmpt_stream_buffer * s = (openmpt_stream_buffer*)stream;
147 	if ( !s ) {
148 		return -1;
149 	}
150 	return s->file_pos;
151 }
152 
openmpt_stream_buffer_init(openmpt_stream_buffer * buffer,const void * file_data,int64_t file_size)153 static void openmpt_stream_buffer_init( openmpt_stream_buffer * buffer, const void * file_data, int64_t file_size ) {
154 	memset( buffer, 0, sizeof( openmpt_stream_buffer ) );
155 	buffer->file_data = file_data;
156 	buffer->file_size = file_size;
157 	buffer->file_pos = 0;
158 	buffer->prefix_size = file_size;
159 	buffer->overflow = 0;
160 }
161 
162 #define openmpt_stream_buffer_init_prefix_only( buffer_, prefix_data_, prefix_size_, file_size_ ) do { \
163 	openmpt_stream_buffer_init( (buffer_), (prefix_data_), (file_size_) ); \
164 	(buffer_)->prefix_size = (prefix_size_); \
165 } while(0)
166 
167 #define openmpt_stream_buffer_overflowed( buffer_ ) ( (buffer_)->overflow )
168 
169 /*! \brief Provide openmpt_stream_callbacks for in-memoy buffers
170  *
171  * Fills openmpt_stream_callbacks suitable for passing an in-memory buffer as a stream parameter to functions doing file input/output.
172  *
173  * \remarks The stream argument must be passed as `(void*)(openmpt_stream_buffer*)stream_buffer`.
174  * \sa \ref libopenmpt_c_fileio
175  * \sa openmpt_stream_callbacks
176  * \sa openmpt_could_open_probability2
177  * \sa openmpt_probe_file_header_from_stream
178  * \sa openmpt_module_create2
179  */
openmpt_stream_get_buffer_callbacks(void)180 static openmpt_stream_callbacks openmpt_stream_get_buffer_callbacks(void) {
181 	openmpt_stream_callbacks retval;
182 	memset( &retval, 0, sizeof( openmpt_stream_callbacks ) );
183 	retval.read = openmpt_stream_buffer_read_func;
184 	retval.seek = openmpt_stream_buffer_seek_func;
185 	retval.tell = openmpt_stream_buffer_tell_func;
186 	return retval;
187 }
188 
189 #ifdef __cplusplus
190 }
191 #endif
192 
193 /*!
194  * @}
195  */
196 
197 #endif /* LIBOPENMPT_STREAM_CALLBACKS_BUFFER_H */
198 
199