2 * Copyright (c) 2005 Tilman Sauerbeck (tilman at code-monkey de)
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 * 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 #define CHECK_READABLE(ef) \
38 switch (eet_mode_get (*ef)) { \
39 case EET_FILE_MODE_READ: \
40 case EET_FILE_MODE_READ_WRITE: \
43 rb_raise (rb_eIOError, "permission denied"); \
46 #ifdef WORDS_BIGENDIAN
48 ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
49 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
50 #else /* !WORDS_BIGENDIAN */
51 # define BSWAP32(x) (x)
52 #endif /* WORDS_BIGENDIAN */
54 static VALUE c_close (VALUE self);
56 static VALUE cStream, cChunk,
57 eEetError, eNameError, ePropError,
58 sym_lossy, sym_level, sym_quality, sym_char, sym_short,
59 sym_long_long, sym_double;
60 static ID id_include, id_to_s, id_keys, id_pack,
61 id_to_eet_chunks, id_to_eet_name, id_to_eet_properties,
65 c_free (Eet_File **ef)
82 return Data_Make_Struct (klass, Eet_File *, NULL, c_free, ef);
87 * Eet::File.open(file [, mode] ) -> ef or nil
88 * Eet::File.open(file [, mode] ) { |ef| block } -> nil
90 * If a block isn't specified, Eet::File.open is a synonym for
92 * If a block is given, it will be invoked with the
93 * Eet::File object as a parameter, and the file will be
94 * automatically closed when the block terminates. The call always
95 * returns +nil+ in this case.
98 c_open (int argc, VALUE *argv, VALUE klass)
100 VALUE obj = rb_class_new_instance (argc, argv, klass);
102 if (rb_block_given_p ())
103 return rb_ensure (rb_yield, obj, c_close, obj);
110 * Eet::File.new(file [, mode] ) -> ef or nil
112 * Creates an Eet::File object for _file_.
114 * _file_ is opened with the specified mode (defaulting to "r").
115 * Possible values for _mode_ are "r" for read-only access,
116 * "w" for write-only access and "r+" for read/write access.
119 c_init (int argc, VALUE *argv, VALUE self)
121 VALUE file = Qnil, mode = Qnil;
122 Eet_File **ef = NULL;
123 Eet_File_Mode m = EET_FILE_MODE_READ;
124 const char *tmp, *cfile;
126 Data_Get_Struct (self, Eet_File *, ef);
128 rb_scan_args (argc, argv, "11", &file, &mode);
130 cfile = StringValuePtr (file);
133 tmp = StringValuePtr (mode);
134 if (!strcmp (tmp, "r+"))
135 m = EET_FILE_MODE_READ_WRITE;
136 else if (!strcmp (tmp, "w"))
137 m = EET_FILE_MODE_WRITE;
138 else if (strcmp (tmp, "r"))
139 rb_raise (rb_eArgError, "illegal access mode %s", tmp);
144 *ef = eet_open (cfile, m);
147 case EET_FILE_MODE_READ_WRITE:
148 case EET_FILE_MODE_WRITE:
149 tmp = "Permission denied - %s";
152 tmp = "File not found - %s";
156 rb_raise (rb_eRuntimeError, tmp, cfile);
166 * Closes _ef_ and flushes any pending writes.
167 * _ef_ is unavailable for any further data operations;
168 * an +IOError+ is raised if such an attempt is made.
170 * Eet::File objects are automatically closed when they
171 * are claimed by the garbage collector.
176 Eet_File **ef = NULL;
178 Data_Get_Struct (self, Eet_File *, ef);
190 get_keys (Eet_File *ef, char *glob)
196 keys = eet_list (ef, glob, &count);
197 ret = rb_ary_new2 (count);
199 for (i = 0; i < count; i++)
200 rb_ary_store (ret, i, rb_str_new2 (keys[i]));
209 * ef.entries -> array
211 * Returns an Array with the keys of the entries in _ef_.
212 * If the keys cannot be retrieved, an +IOError+ is raised.
215 c_entries (VALUE self)
217 Eet_File **ef = NULL;
219 Data_Get_Struct (self, Eet_File *, ef);
223 return get_keys (*ef, "*");
230 * Returns an Array with the keys of entries in _ef_ that match the
232 * If the keys cannot be retrieved, an +IOError+ is raised.
235 c_glob (VALUE self, VALUE glob)
237 Eet_File **ef = NULL;
239 Data_Get_Struct (self, Eet_File *, ef);
243 return get_keys (*ef, StringValuePtr (glob));
248 * ef.delete(key) -> ef
250 * Deletes the entry from _ef_ that is stored as _key_.
251 * If the entry cannot be deleted, an +IOError+ is raised,
252 * otherwise _ef_ is returned.
255 c_delete (VALUE self, VALUE key)
257 Eet_File **ef = NULL;
260 Data_Get_Struct (self, Eet_File *, ef);
263 ckey = StringValuePtr (key);
266 if (!eet_delete (*ef, ckey))
267 rb_raise (rb_eIOError, "cannot delete entry - %s", ckey);
274 * ef.read(key) -> string
276 * Reads an entry from _ef_ that is stored as _key_.
277 * If the data cannot be read, an +IOError+ is raised,
278 * otherwise a String is returned that contains the data.
281 c_read (VALUE self, VALUE key)
284 Eet_File **ef = NULL;
289 Data_Get_Struct (self, Eet_File *, ef);
292 ckey = StringValuePtr (key);
295 data = eet_read (*ef, ckey, &size);
297 rb_raise (rb_eIOError, "cannot read entry - %s", ckey);
299 ret = rb_str_new (data, size);
308 * ef.write(key, data [, compress] ) -> integer
310 * Stores _data_ in _ef_ as _key_.
311 * If _compress_ is true (which is the default), the data will be
313 * If the data cannot be written, an +IOError+ is raised,
314 * otherwise a the number of bytes written is returned.
317 c_write (int argc, VALUE *argv, VALUE self)
319 VALUE key = Qnil, buf = Qnil, comp = Qnil;
320 Eet_File **ef = NULL;
324 Data_Get_Struct (self, Eet_File *, ef);
327 rb_scan_args (argc, argv, "21", &key, &buf, &comp);
332 ckey = StringValuePtr (key);
334 cbuf = StringValuePtr (buf);
336 n = eet_write (*ef, ckey,
337 cbuf, RSTRING (buf)->len,
340 rb_raise (rb_eIOError, "couldn't write to file");
347 * ef.read_image(key) -> array
349 * Reads an image entry from _ef_ that is stored as _key_.
350 * If the data cannot be read, an +IOError+ is raised,
351 * otherwise an Array is returned that contains the image data,
352 * the image width, the image height, a boolean that indicates
353 * whether the image has an alpha channel or not and a hash
354 * that contains the compression options that were used to store
355 * the image (see Eet::File#write_image).
358 c_read_image (VALUE self, VALUE key)
361 Eet_File **ef = NULL;
364 unsigned int w = 0, h = 0;
365 int has_alpha = 0, level = 0, quality = 0, lossy = 0;
367 Data_Get_Struct (self, Eet_File *, ef);
370 ckey = StringValuePtr (key);
373 data = eet_data_image_read (*ef, ckey, &w, &h,
374 &has_alpha, &level, &quality,
377 rb_raise (rb_eIOError, "cannot read entry - %s", ckey);
379 comp = rb_hash_new ();
380 rb_hash_aset (comp, sym_lossy, INT2FIX (lossy));
381 rb_hash_aset (comp, sym_level, INT2FIX (level));
382 rb_hash_aset (comp, sym_quality, INT2FIX (quality));
384 ret = rb_ary_new3 (5, rb_str_new (data, w * h * 4),
385 INT2FIX (w), INT2FIX (h),
386 has_alpha ? Qtrue : Qfalse, comp);
394 * ef.write_image(key, image_data, w, h [, alpha] [, comp] ) -> integer
396 * Stores _image_data_ with width _w_ and height _h_ in _ef_ as _key_.
397 * Pass true for _alpha_ if the image contains an alpha channel.
398 * You can control how the image is compressed by passing _comp_, which
399 * is a hash whose :lossy entry is true if the image should be
400 * compressed lossily. If :lossy is true, the :quality entry
401 * (0-100, default 100) sets the compression quality.
402 * If :lossy is false, the :level entry (0-9, default 9) sets the
403 * compression level. If _comp_ isn't passed, then the
404 * image is stored losslessly.
405 * If the data cannot be written, an +IOError+ is raised,
406 * otherwise a the number of bytes written is returned.
409 c_write_image (int argc, VALUE *argv, VALUE self)
411 VALUE key = Qnil, buf = Qnil, w = Qnil, h = Qnil, has_alpha = Qnil;
412 VALUE comp = Qnil, tmp;
413 Eet_File **ef = NULL;
415 int n, lossy = 0, level = 9, quality = 100;
417 Data_Get_Struct (self, Eet_File *, ef);
420 rb_scan_args (argc, argv, "42", &key, &buf, &w, &h, &has_alpha,
423 if (NIL_P (has_alpha))
426 ckey = StringValuePtr (key);
428 cbuf = StringValuePtr (buf);
429 Check_Type (w, T_FIXNUM);
430 Check_Type (h, T_FIXNUM);
433 Check_Type (comp, T_HASH);
435 tmp = rb_hash_aref (comp, sym_lossy);
437 lossy = FIX2INT (tmp);
439 tmp = rb_hash_aref (comp, sym_level);
441 level = FIX2INT (tmp);
443 tmp = rb_hash_aref (comp, sym_quality);
445 quality = FIX2INT (tmp);
448 if (!RSTRING (buf)->len)
451 n = eet_data_image_write (*ef, ckey, cbuf,
452 FIX2INT (w), FIX2INT (h),
454 level, quality, lossy);
456 rb_raise (rb_eIOError, "couldn't write to file");
462 chunk_init (VALUE self, VALUE tag, VALUE data)
464 long tag_len, data_len, tmp;
469 if (rb_funcall (tag, id_include, 1, INT2FIX (0)) == Qtrue)
470 rb_raise (rb_eArgError, "tag must not contain binary zeroes");
472 /* libeet uses a signed 32bit integer to store the
473 * chunk size, so make sure we don't overflow it
475 tag_len = RSTRING (tag)->len;
476 data_len = RSTRING (data)->len;
477 tmp = tag_len + 1 + data_len;
479 if (tmp < tag_len || tmp < data_len || tmp < 1 || tmp >= 2147483647L)
480 rb_raise (rb_eArgError, "tag or data too long");
482 rb_ivar_set (self, id_tag, rb_str_dup_frozen (tag));
483 rb_ivar_set (self, id_data, rb_str_dup_frozen (data));
489 chunk_to_s (VALUE self)
492 unsigned int size, buf_len;
494 struct RString *tag, *data;
496 tmp = rb_ivar_get (self, id_tag);
499 tmp = rb_ivar_get (self, id_data);
500 data = RSTRING (tmp);
502 buf_len = 9 + tag->len + data->len;
503 ret = rb_str_buf_new (buf_len);
505 buf = (unsigned char *) RSTRING (ret)->ptr;
506 RSTRING (ret)->len = buf_len;
508 memcpy (buf, "CHnK", 4);
511 size = tag->len + data->len + 1;
512 size = BSWAP32 (size);
513 memcpy (buf, &size, 4);
516 memcpy (buf, tag->ptr, tag->len);
521 memcpy (buf, data->ptr, data->len);
527 for_each_prop (VALUE tag, VALUE arg, VALUE stream)
529 VALUE value, type, tmp;
531 if (rb_obj_is_kind_of (arg, rb_cArray) == Qfalse)
532 rb_raise (ePropError, "hash value is not an array");
534 value = rb_ary_entry (arg, 0);
538 type = rb_ary_entry (arg, 1);
539 tmp = rb_funcall (value, id_to_eet_chunks, 2, tag, type);
541 rb_ary_concat (stream, tmp);
548 * object.to_eet -> string
550 * Serializes the receiver to EET format.
553 c_to_eet (VALUE self)
555 VALUE props, name, stream, chunk, args[2];
556 #ifndef HAVE_RB_HASH_FOREACH
561 props = rb_funcall (self, id_to_eet_properties, 0);
563 if (rb_obj_is_kind_of (props, rb_cHash) == Qfalse ||
564 !RHASH (props)->tbl->num_entries)
565 rb_raise (ePropError, "invalid EET properties");
567 name = rb_funcall (self, id_to_eet_name, 0);
570 if (!RSTRING (name)->len ||
571 rb_funcall (name, id_include, 1, INT2FIX (0)))
572 rb_raise (eNameError, "invalid EET name");
574 stream = rb_class_new_instance (0, NULL, cStream);
576 #ifdef HAVE_RB_HASH_FOREACH
577 rb_hash_foreach (props, for_each_prop, stream);
579 keys = RARRAY (rb_funcall (props, id_keys, 0));
581 for (i = 0; i < keys->len; i++)
582 for_each_prop (keys->ptr[i],
583 rb_hash_aref (props, keys->ptr[i]),
588 args[1] = rb_ary_to_s (stream);
590 rb_ary_clear (stream); /* give the GC a hand... */
592 chunk = rb_class_new_instance (2, args, cChunk);
594 return rb_funcall (chunk, id_to_s, 0);
598 int_to_eet_chunks (int argc, VALUE *argv, VALUE self)
600 VALUE tag, type = Qnil, ary, args[2], chunk;
603 rb_scan_args (argc, argv, "11", &tag, &type);
605 ary = rb_ary_new3 (1, self);
607 if (type == sym_char)
609 else if (type == sym_short)
611 else if (type == sym_long_long)
615 args[1] = rb_funcall (ary, id_pack, 1, rb_str_new2 (cfmt));
616 chunk = rb_class_new_instance (2, args, cChunk);
618 return rb_ary_new3 (1, chunk);
622 float_to_eet_chunks (int argc, VALUE *argv, VALUE self)
624 VALUE tag, type = Qnil, args[2], chunk;
629 rb_scan_args (argc, argv, "11", &tag, &type);
633 /* switch locale to make sure we get proper snprintf output */
634 loc = setlocale (LC_NUMERIC, "C");
636 len = snprintf (buf, sizeof (buf) - 1, "%a",
637 type == sym_double ? d : (float) d);
640 setlocale (LC_NUMERIC, loc);
645 args[1] = rb_str_new (buf, len);
646 chunk = rb_class_new_instance (2, args, cChunk);
648 return rb_ary_new3 (1, chunk);
656 m = rb_define_module ("Eet");
658 c = rb_define_class_under (m, "File", rb_cObject);
659 rb_define_alloc_func (c, c_alloc);
660 rb_define_singleton_method (c, "open", c_open, -1);
661 rb_define_method (c, "initialize", c_init, -1);
662 rb_define_method (c, "close", c_close, 0);
663 rb_define_method (c, "entries", c_entries, 0);
664 rb_define_method (c, "[]", c_glob, 1);
665 rb_define_method (c, "delete", c_delete, 1);
666 rb_define_method (c, "read", c_read, 1);
667 rb_define_method (c, "write", c_write, -1);
668 rb_define_method (c, "read_image", c_read_image, 1);
669 rb_define_method (c, "write_image", c_write_image, -1);
671 cStream = rb_define_class_under (m, "Stream", rb_cArray);
673 cChunk = rb_define_class_under (m, "Chunk", rb_cObject);
674 rb_define_method (cChunk, "initialize", chunk_init, 2);
675 rb_define_method (cChunk, "to_s", chunk_to_s, 0);
677 rb_define_attr (cChunk, "tag", 1, 0);
678 rb_define_attr (cChunk, "data", 1, 0);
680 rb_define_method (rb_cObject, "to_eet", c_to_eet, 0);
682 rb_define_method (rb_cInteger, "to_eet_chunks", int_to_eet_chunks, -1);
683 rb_define_method (rb_cFloat, "to_eet_chunks", float_to_eet_chunks, -1);
685 eEetError = rb_define_class_under (m, "EetError", rb_eStandardError);
686 eNameError = rb_define_class_under (m, "NameError", eEetError);
687 ePropError = rb_define_class_under (m, "PropertyError", eEetError);
689 id_include = rb_intern ("include?");
690 id_to_s = rb_intern ("to_s");
691 id_keys = rb_intern ("keys");
692 id_pack = rb_intern ("pack");
693 id_to_eet_chunks = rb_intern ("to_eet_chunks");
694 id_to_eet_name = rb_intern ("to_eet_name");
695 id_to_eet_properties = rb_intern ("to_eet_properties");
696 id_tag = rb_intern ("@tag");
697 id_data = rb_intern ("@data");
698 sym_lossy = ID2SYM (rb_intern ("lossy"));
699 sym_level = ID2SYM (rb_intern ("level"));
700 sym_quality = ID2SYM (rb_intern ("quality"));
701 sym_char = ID2SYM (rb_intern ("char"));
702 sym_short = ID2SYM (rb_intern ("short"));
703 sym_long_long = ID2SYM (rb_intern ("long_long"));
704 sym_double = ID2SYM (rb_intern ("double"));