f7860d6d2f2ff0c23799ea2868c1f7ab6f4f4aee
[ruby-eet.git] / ext / ext.c
1 /*
2  * $Id: ext.c 1 2005-03-26 01:45:38Z tilman $
3  *
4  * Copyright (c) 2005 Tilman Sauerbeck (tilman at code-monkey de)
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25
26 #include <Eet.h>
27 #include <ruby.h>
28
29 #define CHECK_KEY(key) \
30         Check_Type ((key), T_STRING); \
31         if (rb_funcall ((key), rb_intern ("include?"), \
32                             1, INT2FIX (0)) == Qtrue) \
33                 rb_raise (rb_eArgError, "key must not contain binary zeroes");
34
35 static VALUE c_close (VALUE self);
36
37 static void
38 c_free (Eet_File **ef)
39 {
40         if (*ef) {
41                 eet_close (*ef);
42                 *ef = NULL;
43
44                 eet_shutdown ();
45         }
46
47         free (ef);
48 }
49
50 static VALUE
51 c_alloc (VALUE klass)
52 {
53         Eet_File **ef = NULL;
54
55         return Data_Make_Struct (klass, Eet_File *, NULL, c_free, ef);
56 }
57
58 /*
59  * call-seq:
60  *  Eet::File.open(file [, mode] )                -> ef or nil
61  *  Eet::File.open(file [, mode] ) { |ef| block } -> nil
62  *
63  * If a block isn't specified, Eet::File.open is a synonym for
64  * Eet::File.new.
65  * If a block is given, it will be invoked with the
66  * Eet::File object as a parameter, and the file will be
67  * automatically closed when the block terminates. The call always
68  * returns +nil+ in this case.
69  */
70 static VALUE
71 c_open (int argc, VALUE *argv, VALUE klass)
72 {
73         VALUE obj = rb_class_new_instance (argc, argv, klass);
74
75         if (rb_block_given_p ())
76                 return rb_ensure (rb_yield, obj, c_close, obj);
77         else
78                 return obj;
79 }
80
81 /*
82  * call-seq:
83  *  Eet::File.new(file [, mode] ) -> ef or nil
84  *
85  * Creates an Eet::File object for _file_.
86  *
87  * _file_ is opened with the specified mode (defaulting to "r").
88  * Possible values for _mode_ are "r" for read-only access,
89  * "w" for write-only access and "r+" for read/write access.
90  */
91 static VALUE
92 c_init (int argc, VALUE *argv, VALUE self)
93 {
94         VALUE file = Qnil, mode = Qnil;
95         Eet_File **ef = NULL;
96         Eet_File_Mode m = EET_FILE_MODE_READ;
97         const char *tmp;
98
99         Data_Get_Struct (self, Eet_File *, ef);
100
101         rb_scan_args (argc, argv, "11", &file, &mode);
102
103         Check_Type (file, T_STRING);
104
105         if (!NIL_P (mode)) {
106                 Check_Type (mode, T_STRING);
107
108                 tmp = StringValuePtr (mode);
109                 if (!strcmp (tmp, "r+"))
110                         m = EET_FILE_MODE_READ_WRITE;
111                 else if (!strcmp (tmp, "w"))
112                         m = EET_FILE_MODE_WRITE;
113                 else if (strcmp (tmp, "r"))
114                         rb_raise (rb_eArgError, "illegal access mode %s", tmp);
115         }
116
117         eet_init ();
118
119         *ef = eet_open (StringValuePtr (file), m);
120         if (!*ef) {
121                 switch (m) {
122                         case EET_FILE_MODE_READ_WRITE:
123                         case EET_FILE_MODE_WRITE:
124                                 tmp = "Permission denied - %s";
125                                 break;
126                         default:
127                                 tmp = "File not found - %s";
128                                 break;
129                 }
130
131                 rb_raise (rb_eRuntimeError, tmp, file);
132         }
133
134         return self;
135 }
136
137 /*
138  * call-seq:
139  *  ef.close -> ef
140  *
141  * Closes _ef_ and flushes any pending writes.
142  * _ef_ is unavailable for any further data operations;
143  * an +IOError+ is raised if such an attempt is made.
144  *
145  * Eet::File objects are automatically closed when they
146  * are claimed by the garbage collector.
147  */
148 static VALUE
149 c_close (VALUE self)
150 {
151         Eet_File **ef = NULL;
152
153         Data_Get_Struct (self, Eet_File *, ef);
154
155         if (!*ef)
156                 rb_raise (rb_eIOError, "closed stream");
157         else {
158                 eet_close (*ef);
159                 *ef = NULL;
160
161                 eet_shutdown ();
162         }
163
164         return self;
165 }
166
167 /*
168  * call-seq:
169  *  ef.list([glob]) -> array
170  *
171  * Returns an Array of entries in _ef_ that match the shell glob
172  * _glob_ (defaulting to "*").
173  */
174 static VALUE
175 c_list (int argc, VALUE *argv, VALUE self)
176 {
177         VALUE glob = Qnil, ret;
178         Eet_File **ef = NULL;
179         char **entries, *tmp = "*";
180         int i, count = 0;
181
182         Data_Get_Struct (self, Eet_File *, ef);
183
184         if (!*ef)
185                 rb_raise (rb_eIOError, "closed stream");
186
187         switch (eet_mode_get (*ef)) {
188                 case EET_FILE_MODE_READ:
189                 case EET_FILE_MODE_READ_WRITE:
190                         break;
191                 default:
192                         rb_raise (rb_eIOError, "cannot list entries");
193         }
194
195         rb_scan_args (argc, argv, "01", &glob);
196
197         if (!NIL_P (glob)) {
198                 Check_Type (glob, T_STRING);
199                 tmp = StringValuePtr (glob);
200         }
201
202         ret = rb_ary_new ();
203
204         entries = eet_list (*ef, tmp, &count);
205
206         for (i = 0; i < count; i++)
207                 rb_ary_push (ret, rb_str_new2 (entries[i]));
208
209         free (entries);
210
211         return ret;
212 }
213
214 /*
215  * call-seq:
216  *  ef.delete(key) -> ef
217  *
218  * Deletes the entry from _ef_ that is stored as _key_.
219  * If the entry cannot be deleted, an +IOError+ is raised,
220  * otherwise _ef_ is returned.
221  */
222 static VALUE
223 c_delete (VALUE self, VALUE key)
224 {
225         Eet_File **ef = NULL;
226         char *tmp;
227
228         Data_Get_Struct (self, Eet_File *, ef);
229
230         if (!*ef)
231                 rb_raise (rb_eIOError, "closed stream");
232
233         CHECK_KEY (key);
234
235         tmp = StringValuePtr (key);
236
237         if (!eet_delete (*ef, tmp))
238                 rb_raise (rb_eIOError, "cannot delete entry - %s", tmp);
239
240         return self;
241 }
242
243 /*
244  * call-seq:
245  *  ef.read(key) -> string
246  *
247  * Reads an entry from _ef_ that is stored as _key_.
248  * If the data cannot be read, an +IOError+ is raised,
249  * otherwise a String is returned that contains the data.
250  */
251 static VALUE
252 c_read (VALUE self, VALUE key)
253 {
254         VALUE ret;
255         Eet_File **ef = NULL;
256         void *data;
257         int size = 0;
258
259         Data_Get_Struct (self, Eet_File *, ef);
260
261         if (!*ef)
262                 rb_raise (rb_eIOError, "closed stream");
263
264         CHECK_KEY (key);
265
266         data = eet_read (*ef, StringValuePtr (key), &size);
267         if (!data)
268                 rb_raise (rb_eIOError, "cannot read entry - %s", key);
269
270         ret = rb_str_new (data, size);
271
272         free (data);
273
274         return ret;
275 }
276
277 /*
278  * call-seq:
279  *  ef.write(key, data [, compress] ) -> integer
280  *
281  * Stores _data_ in _ef_ as _key_.
282  * If _compress_ is true (which is the default), the data will be
283  * compressed.
284  * If the data cannot be written, an +IOError+ is raised,
285  * otherwise a the number of bytes written is returned.
286  */
287 static VALUE
288 c_write (int argc, VALUE *argv, VALUE self)
289 {
290         VALUE key = Qnil, buf = Qnil, comp = Qnil;
291         Eet_File **ef = NULL;
292         int n;
293
294         Data_Get_Struct (self, Eet_File *, ef);
295
296         if (!*ef)
297                 rb_raise (rb_eIOError, "closed stream");
298
299         rb_scan_args (argc, argv, "21", &key, &buf, &comp);
300
301         if (NIL_P (comp))
302                 comp = Qtrue;
303
304         CHECK_KEY (key);
305         Check_Type (buf, T_STRING);
306
307         n = eet_write (*ef, StringValuePtr (key),
308                        StringValuePtr (buf), RSTRING (buf)->len,
309                        comp == Qtrue);
310         if (!n)
311                 rb_raise (rb_eIOError, "couldn't write to file");
312         else
313                 return INT2FIX (n);
314 }
315
316 /*
317  * call-seq:
318  *  ef.read_image(key) -> array
319  *
320  * Reads an image entry from _ef_ that is stored as _key_.
321  * If the data cannot be read, an +IOError+ is raised,
322  * otherwise an Array is returned that contains the image data,
323  * the image width, the image height and a boolean that indicates
324  * whether the image has an alpha channel or not.
325  */
326 static VALUE
327 c_read_image (VALUE self, VALUE key)
328 {
329         VALUE ret;
330         Eet_File **ef = NULL;
331         void *data;
332         int w = 0, h = 0, has_alpha = 0;
333
334         Data_Get_Struct (self, Eet_File *, ef);
335
336         if (!*ef)
337                 rb_raise (rb_eIOError, "closed stream");
338
339         CHECK_KEY (key);
340
341         data = eet_data_image_read (*ef, StringValuePtr (key), &w, &h,
342                                     &has_alpha, NULL, NULL, NULL);
343         if (!data)
344                 rb_raise (rb_eIOError, "cannot read entry - %s", key);
345
346         ret = rb_ary_new3 (4, rb_str_new (data, w * h * 4),
347                                INT2FIX (w), INT2FIX (h),
348                                has_alpha ? Qtrue : Qfalse);
349         free (data);
350
351         return ret;
352 }
353
354 /*
355  * call-seq:
356  *  ef.write_image(key, image_data, w, h [, alpha] ) -> integer
357  *
358  * Stores _image_data_ with width _w_ and height _h_ in _ef_ as _key_.
359  * Pass true for _alpha_ if the image contains an alpha channel.
360  * If the data cannot be written, an +IOError+ is raised,
361  * otherwise a the number of bytes written is returned.
362  */
363 static VALUE
364 c_write_image (int argc, VALUE *argv, VALUE self)
365 {
366         VALUE key = Qnil, buf = Qnil, w = Qnil, h = Qnil, has_alpha = Qnil;
367         Eet_File **ef = NULL;
368         int n;
369
370         Data_Get_Struct (self, Eet_File *, ef);
371
372         if (!*ef)
373                 rb_raise (rb_eIOError, "closed stream");
374
375         rb_scan_args (argc, argv, "41", &key, &buf, &w, &h, &has_alpha);
376
377         if (NIL_P (has_alpha))
378                 has_alpha = Qfalse;
379
380         CHECK_KEY (key);
381         Check_Type (buf, T_STRING);
382         Check_Type (w, T_FIXNUM);
383         Check_Type (h, T_FIXNUM);
384
385         if (!RSTRING (buf)->len)
386                 return INT2FIX (0);
387
388         n = eet_data_image_write (*ef, StringValuePtr (key),
389                                   StringValuePtr (buf),
390                                   FIX2INT (w), FIX2INT (h),
391                                   has_alpha == Qtrue, 9, 0, 0);
392         if (!n)
393                 rb_raise (rb_eIOError, "couldn't write to file");
394         else
395                 return INT2FIX (n);
396 }
397
398 void
399 Init_eet_ext ()
400 {
401         VALUE m, c;
402
403         m = rb_define_module ("Eet");
404
405         c = rb_define_class_under (m, "File", rb_cObject);
406         rb_define_alloc_func (c, c_alloc);
407         rb_define_singleton_method (c, "open", c_open, -1);
408         rb_define_method (c, "initialize", c_init, -1);
409         rb_define_method (c, "close", c_close, 0);
410         rb_define_method (c, "list", c_list, -1);
411         rb_define_method (c, "delete", c_delete, 1);
412         rb_define_method (c, "read", c_read, 1);
413         rb_define_method (c, "write", c_write, -1);
414         rb_define_method (c, "read_image", c_read_image, 1);
415         rb_define_method (c, "write_image", c_write_image, -1);
416 }