2 * $Id: ext.c 35 2005-05-10 18:58:49Z tilman $
4 * Copyright (c) 2005 Tilman Sauerbeck (tilman at code-monkey de)
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:
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
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.
29 #define CHECK_KEY(key) \
30 if (rb_funcall (key, id_include, 1, INT2FIX (0)) == Qtrue) \
31 rb_raise (rb_eArgError, "key must not contain binary zeroes");
33 #define CHECK_CLOSED(ef) \
35 rb_raise (rb_eIOError, "closed stream");
37 #ifdef WORDS_BIGENDIAN
39 ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
40 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
41 #else /* !WORDS_BIGENDIAN */
42 # define BSWAP16(x) (x)
43 # define BSWAP32(x) (x)
44 #endif /* WORDS_BIGENDIAN */
46 static VALUE c_close (VALUE self);
48 static VALUE id_include;
51 c_free (Eet_File **ef)
68 return Data_Make_Struct (klass, Eet_File *, NULL, c_free, ef);
73 * Eet::File.open(file [, mode] ) -> ef or nil
74 * Eet::File.open(file [, mode] ) { |ef| block } -> nil
76 * If a block isn't specified, Eet::File.open is a synonym for
78 * If a block is given, it will be invoked with the
79 * Eet::File object as a parameter, and the file will be
80 * automatically closed when the block terminates. The call always
81 * returns +nil+ in this case.
84 c_open (int argc, VALUE *argv, VALUE klass)
86 VALUE obj = rb_class_new_instance (argc, argv, klass);
88 if (rb_block_given_p ())
89 return rb_ensure (rb_yield, obj, c_close, obj);
96 * Eet::File.new(file [, mode] ) -> ef or nil
98 * Creates an Eet::File object for _file_.
100 * _file_ is opened with the specified mode (defaulting to "r").
101 * Possible values for _mode_ are "r" for read-only access,
102 * "w" for write-only access and "r+" for read/write access.
105 c_init (int argc, VALUE *argv, VALUE self)
107 VALUE file = Qnil, mode = Qnil;
108 Eet_File **ef = NULL;
109 Eet_File_Mode m = EET_FILE_MODE_READ;
110 const char *tmp, *cfile;
112 Data_Get_Struct (self, Eet_File *, ef);
114 rb_scan_args (argc, argv, "11", &file, &mode);
116 cfile = StringValuePtr (file);
119 tmp = StringValuePtr (mode);
120 if (!strcmp (tmp, "r+"))
121 m = EET_FILE_MODE_READ_WRITE;
122 else if (!strcmp (tmp, "w"))
123 m = EET_FILE_MODE_WRITE;
124 else if (strcmp (tmp, "r"))
125 rb_raise (rb_eArgError, "illegal access mode %s", tmp);
130 *ef = eet_open (cfile, m);
133 case EET_FILE_MODE_READ_WRITE:
134 case EET_FILE_MODE_WRITE:
135 tmp = "Permission denied - %s";
138 tmp = "File not found - %s";
142 rb_raise (rb_eRuntimeError, tmp, cfile);
152 * Closes _ef_ and flushes any pending writes.
153 * _ef_ is unavailable for any further data operations;
154 * an +IOError+ is raised if such an attempt is made.
156 * Eet::File objects are automatically closed when they
157 * are claimed by the garbage collector.
162 Eet_File **ef = NULL;
164 Data_Get_Struct (self, Eet_File *, ef);
177 * ef.list([glob]) -> array
179 * Returns an Array of entries in _ef_ that match the shell glob
180 * _glob_ (defaulting to "*").
183 c_list (int argc, VALUE *argv, VALUE self)
185 VALUE glob = Qnil, ret;
186 Eet_File **ef = NULL;
187 char **entries, *tmp = "*";
190 Data_Get_Struct (self, Eet_File *, ef);
193 switch (eet_mode_get (*ef)) {
194 case EET_FILE_MODE_READ:
195 case EET_FILE_MODE_READ_WRITE:
198 rb_raise (rb_eIOError, "cannot list entries");
201 rb_scan_args (argc, argv, "01", &glob);
204 tmp = StringValuePtr (glob);
208 entries = eet_list (*ef, tmp, &count);
210 for (i = 0; i < count; i++)
211 rb_ary_push (ret, rb_str_new2 (entries[i]));
220 * ef.delete(key) -> ef
222 * Deletes the entry from _ef_ that is stored as _key_.
223 * If the entry cannot be deleted, an +IOError+ is raised,
224 * otherwise _ef_ is returned.
227 c_delete (VALUE self, VALUE key)
229 Eet_File **ef = NULL;
232 Data_Get_Struct (self, Eet_File *, ef);
235 ckey = StringValuePtr (key);
238 if (!eet_delete (*ef, ckey))
239 rb_raise (rb_eIOError, "cannot delete entry - %s", ckey);
246 * ef.read(key) -> string
248 * Reads an entry from _ef_ that is stored as _key_.
249 * If the data cannot be read, an +IOError+ is raised,
250 * otherwise a String is returned that contains the data.
253 c_read (VALUE self, VALUE key)
256 Eet_File **ef = NULL;
261 Data_Get_Struct (self, Eet_File *, ef);
264 ckey = StringValuePtr (key);
267 data = eet_read (*ef, ckey, &size);
269 rb_raise (rb_eIOError, "cannot read entry - %s", ckey);
271 ret = rb_str_new (data, size);
280 * ef.write(key, data [, compress] ) -> integer
282 * Stores _data_ in _ef_ as _key_.
283 * If _compress_ is true (which is the default), the data will be
285 * If the data cannot be written, an +IOError+ is raised,
286 * otherwise a the number of bytes written is returned.
289 c_write (int argc, VALUE *argv, VALUE self)
291 VALUE key = Qnil, buf = Qnil, comp = Qnil;
292 Eet_File **ef = NULL;
296 Data_Get_Struct (self, Eet_File *, ef);
299 rb_scan_args (argc, argv, "21", &key, &buf, &comp);
304 ckey = StringValuePtr (key);
306 cbuf = StringValuePtr (buf);
308 n = eet_write (*ef, ckey,
309 cbuf, RSTRING (buf)->len,
312 rb_raise (rb_eIOError, "couldn't write to file");
319 * ef.read_image(key) -> array
321 * Reads an image entry from _ef_ that is stored as _key_.
322 * If the data cannot be read, an +IOError+ is raised,
323 * otherwise an Array is returned that contains the image data,
324 * the image width, the image height, a boolean that indicates
325 * whether the image has an alpha channel or not and a hash
326 * that contains the compression options that were used to store
327 * the image (see Eet::File#write_image).
330 c_read_image (VALUE self, VALUE key)
333 Eet_File **ef = NULL;
336 int w = 0, h = 0, has_alpha = 0, level = 0, quality = 0, lossy = 0;
338 Data_Get_Struct (self, Eet_File *, ef);
341 ckey = StringValuePtr (key);
344 data = eet_data_image_read (*ef, ckey, &w, &h,
345 &has_alpha, &level, &quality,
348 rb_raise (rb_eIOError, "cannot read entry - %s", ckey);
350 comp = rb_hash_new ();
351 rb_hash_aset (comp, ID2SYM (rb_intern ("lossy")), INT2FIX (lossy));
352 rb_hash_aset (comp, ID2SYM (rb_intern ("level")), INT2FIX (level));
353 rb_hash_aset (comp, ID2SYM (rb_intern ("quality")), INT2FIX (quality));
355 ret = rb_ary_new3 (5, rb_str_new (data, w * h * 4),
356 INT2FIX (w), INT2FIX (h),
357 has_alpha ? Qtrue : Qfalse, comp);
365 * ef.write_image(key, image_data, w, h [, alpha] [, comp] ) -> integer
367 * Stores _image_data_ with width _w_ and height _h_ in _ef_ as _key_.
368 * Pass true for _alpha_ if the image contains an alpha channel.
369 * You can control how the image is compressed by passing _comp_, which
370 * is a hash whose :lossy entry is true if the image should be
371 * compressed lossily. If :lossy is true, the :quality entry
372 * (0-100, default 100) sets the compression quality.
373 * If :lossy is false, the :level entry (0-9, default 9) sets the
374 * compression level. If _comp_ isn't passed, then the
375 * image is stored losslessly.
376 * If the data cannot be written, an +IOError+ is raised,
377 * otherwise a the number of bytes written is returned.
380 c_write_image (int argc, VALUE *argv, VALUE self)
382 VALUE key = Qnil, buf = Qnil, w = Qnil, h = Qnil, has_alpha = Qnil;
383 VALUE comp = Qnil, tmp;
384 Eet_File **ef = NULL;
386 int n, lossy = 0, level = 9, quality = 100;
388 Data_Get_Struct (self, Eet_File *, ef);
391 rb_scan_args (argc, argv, "42", &key, &buf, &w, &h, &has_alpha,
394 if (NIL_P (has_alpha))
397 ckey = StringValuePtr (key);
399 cbuf = StringValuePtr (buf);
400 Check_Type (w, T_FIXNUM);
401 Check_Type (h, T_FIXNUM);
404 Check_Type (comp, T_HASH);
406 tmp = rb_hash_aref (comp, ID2SYM (rb_intern ("lossy")));
408 lossy = FIX2INT (tmp);
410 tmp = rb_hash_aref (comp, ID2SYM (rb_intern ("level")));
412 level = FIX2INT (tmp);
414 tmp = rb_hash_aref (comp, ID2SYM (rb_intern ("quality")));
416 quality = FIX2INT (tmp);
419 if (!RSTRING (buf)->len)
422 n = eet_data_image_write (*ef, ckey, cbuf,
423 FIX2INT (w), FIX2INT (h),
425 level, quality, lossy);
427 rb_raise (rb_eIOError, "couldn't write to file");
433 stream_serialize (VALUE self)
436 struct RArray *stream;
437 static ID id_serialize;
440 ret = rb_str_new2 ("");
442 stream = RARRAY (self);
447 id_serialize = rb_intern ("serialize");
449 for (i = 0; i < stream->len; i++) {
450 VALUE str = rb_funcall (stream->ptr[i], id_serialize, 0, NULL);
452 rb_str_append (ret, str);
459 chunk_serialize (VALUE self)
462 unsigned int size, buf_len;
464 struct RString *tag, *data;
465 static ID id_tag, id_data;
468 id_tag = rb_intern ("@tag");
471 id_data = rb_intern ("@data");
473 tmp = rb_ivar_get (self, id_tag);
477 tmp = rb_ivar_get (self, id_data);
479 data = RSTRING (tmp);
481 buf_len = 9 + tag->len + data->len;
482 ret = rb_str_buf_new (buf_len);
484 buf = RSTRING (ret)->ptr;
485 RSTRING (ret)->len = buf_len;
487 memcpy (buf, "CHnK", 4);
490 size = tag->len + data->len + 1;
491 size = BSWAP32 (size);
492 memcpy (buf, &size, 4);
495 memcpy (buf, tag->ptr, tag->len);
500 memcpy (buf, data->ptr, data->len);
510 m = rb_define_module ("Eet");
512 c = rb_define_class_under (m, "File", rb_cObject);
513 rb_define_alloc_func (c, c_alloc);
514 rb_define_singleton_method (c, "open", c_open, -1);
515 rb_define_method (c, "initialize", c_init, -1);
516 rb_define_method (c, "close", c_close, 0);
517 rb_define_method (c, "list", c_list, -1);
518 rb_define_method (c, "delete", c_delete, 1);
519 rb_define_method (c, "read", c_read, 1);
520 rb_define_method (c, "write", c_write, -1);
521 rb_define_method (c, "read_image", c_read_image, 1);
522 rb_define_method (c, "write_image", c_write_image, -1);
524 c = rb_define_class_under (m, "Stream", rb_cArray);
525 rb_define_method (c, "serialize", stream_serialize, 0);
527 c = rb_define_class_under (m, "Chunk", rb_cObject);
528 rb_define_method (c, "serialize", chunk_serialize, 0);
530 id_include = rb_intern ("include?");