#include #include #include #include "crn_decomp.h" #include ID sym_at_data, sym_new; VALUE stFileInfo, stTextureInfo, stLevelInfo; static void set_format_constant(VALUE module) { rb_const_set(module, rb_intern("INVALID"), LONG2NUM(cCRNFmtInvalid)); rb_const_set(module, rb_intern("FIRST_VALID"), LONG2NUM(cCRNFmtFirstValid)); rb_const_set(module, rb_intern("DXT1"), LONG2NUM(cCRNFmtDXT1)); rb_const_set(module, rb_intern("DXT3"), LONG2NUM(cCRNFmtDXT3)); rb_const_set(module, rb_intern("DXT5"), LONG2NUM(cCRNFmtDXT5)); rb_const_set(module, rb_intern("DXT5_CCXY"), LONG2NUM(cCRNFmtDXT5_CCxY)); rb_const_set(module, rb_intern("DXT5_XGXR"), LONG2NUM(cCRNFmtDXT5_xGxR)); rb_const_set(module, rb_intern("DXT5_XGBR"), LONG2NUM(cCRNFmtDXT5_xGBR)); rb_const_set(module, rb_intern("DXT5_AGBR"), LONG2NUM(cCRNFmtDXT5_AGBR)); rb_const_set(module, rb_intern("DXN_XY"), LONG2NUM(cCRNFmtDXN_XY)); rb_const_set(module, rb_intern("DXN_YX"), LONG2NUM(cCRNFmtDXN_YX)); rb_const_set(module, rb_intern("DXT5A"), LONG2NUM(cCRNFmtDXT5A)); rb_const_set(module, rb_intern("ETC1"), LONG2NUM(cCRNFmtETC1)); rb_const_set(module, rb_intern("ETC2"), LONG2NUM(cCRNFmtETC2)); rb_const_set(module, rb_intern("ETC2A"), LONG2NUM(cCRNFmtETC2A)); rb_const_set(module, rb_intern("ETC1S"), LONG2NUM(cCRNFmtETC1S)); rb_const_set(module, rb_intern("ETC2AS"), LONG2NUM(cCRNFmtETC2AS)); rb_const_set(module, rb_intern("TOTAL"), LONG2NUM(cCRNFmtTotal)); } static VALUE rb_cCrunchStream_file_info(VALUE self) { VALUE str = rb_ivar_get(self, sym_at_data); crnd::crn_file_info file_info; if (!crnd::crnd_validate_file(RSTRING_PTR(str), RSTRING_LENINT(str), &file_info)) { rb_raise(rb_eRuntimeError, "cannot get file info (invalid file?)"); return Qnil; } VALUE level_compressed_size = rb_ary_new2(file_info.m_levels); for (uint32_t i = 0; i < file_info.m_levels; i++) rb_ary_push(level_compressed_size, UINT2NUM(file_info.m_level_compressed_size[i])); VALUE args[] = { UINT2NUM(file_info.m_struct_size), UINT2NUM(file_info.m_actual_data_size), UINT2NUM(file_info.m_header_size), UINT2NUM(file_info.m_total_palette_size), UINT2NUM(file_info.m_tables_size), UINT2NUM(file_info.m_levels), level_compressed_size, UINT2NUM(file_info.m_color_endpoint_palette_entries), UINT2NUM(file_info.m_color_selector_palette_entries), UINT2NUM(file_info.m_alpha_endpoint_palette_entries), UINT2NUM(file_info.m_alpha_selector_palette_entries) }; return rb_class_new_instance(sizeof(args) / sizeof(VALUE), args, stFileInfo); } static VALUE rb_cCrunchStream_texture_info(VALUE self) { VALUE str = rb_ivar_get(self, sym_at_data); crnd::crn_texture_info texture_info; if (!crnd::crnd_get_texture_info(RSTRING_PTR(str), RSTRING_LENINT(str), &texture_info)) { rb_raise(rb_eRuntimeError, "cannot get texture info (invalid file?)"); return Qnil; } VALUE args[] = { UINT2NUM(texture_info.m_struct_size), UINT2NUM(texture_info.m_width), UINT2NUM(texture_info.m_height), UINT2NUM(texture_info.m_levels), UINT2NUM(texture_info.m_faces), UINT2NUM(texture_info.m_bytes_per_block), UINT2NUM(texture_info.m_userdata0), UINT2NUM(texture_info.m_userdata1), UINT2NUM(texture_info.m_format) }; return rb_class_new_instance(sizeof(args) / sizeof(VALUE), args, stTextureInfo); } static VALUE rb_cCrunchStream_level_info(VALUE self, VALUE rb_level) { VALUE str = rb_ivar_get(self, sym_at_data); crnd::crn_level_info level_info; if (!crnd::crnd_get_level_info(RSTRING_PTR(str), RSTRING_LENINT(str), NUM2UINT(rb_level), &level_info)) { rb_raise(rb_eRuntimeError, "cannot get level info (invalid file or invalid level?)"); return Qnil; } VALUE args[] = { UINT2NUM(level_info.m_struct_size), UINT2NUM(level_info.m_width), UINT2NUM(level_info.m_height), UINT2NUM(level_info.m_faces), UINT2NUM(level_info.m_blocks_x), UINT2NUM(level_info.m_blocks_y), UINT2NUM((level_info.m_width + level_info.m_blocks_x - 1) / level_info.m_blocks_x), UINT2NUM((level_info.m_height + level_info.m_blocks_y - 1) / level_info.m_blocks_y), UINT2NUM(level_info.m_bytes_per_block), UINT2NUM(level_info.m_format) }; return rb_class_new_instance(sizeof(args) / sizeof(VALUE), args, stLevelInfo); } static VALUE rb_cCrunchStream_unpack_level(VALUE self, VALUE rb_level) { VALUE str = rb_ivar_get(self, sym_at_data); crnd::crn_level_info level_info; if (!crnd::crnd_get_level_info(RSTRING_PTR(str), RSTRING_LENINT(str), NUM2UINT(rb_level), &level_info)) { rb_raise(rb_eRuntimeError, "cannot get level info (invalid file or invalid level?)"); return Qnil; } uint32_t pitch_size = level_info.m_blocks_x * level_info.m_bytes_per_block; uint32_t size = pitch_size * level_info.m_blocks_y; VALUE ret = rb_str_buf_new(size); void *ret_ptr = (void*)RSTRING_PTR(ret); crnd::crnd_unpack_context context = crnd::crnd_unpack_begin(RSTRING_PTR(str), RSTRING_LENINT(str)); if (context == nullptr) { rb_raise(rb_eRuntimeError, "context creation error"); return Qnil; } if (!crnd::crnd_unpack_level(context, &ret_ptr, size, pitch_size, 0)) { rb_raise(rb_eRuntimeError, "unpack error"); return Qnil; } crnd::crnd_unpack_end(context); rb_str_set_len(ret, size); return ret; } extern "C" { static VALUE create_rb_struct(const int argc, const char **argv) { VALUE *argv_values = (VALUE*)malloc(sizeof(VALUE*) * argc); for (int i = 0; i < argc; i++) argv_values[i] = ID2SYM(rb_intern(argv[i])); VALUE ret = rb_funcall2(rb_cStruct, sym_new, argc, argv_values); free(argv_values); return ret; } static VALUE rb_cCrunchStream_initialize(VALUE self, VALUE rb_data) { Check_Type(rb_data, T_STRING); rb_ivar_set(self, sym_at_data, rb_data); return self; } void Init_crunch() { sym_new = rb_intern("new"); sym_at_data = rb_intern("@data"); VALUE mMikunyan = rb_define_module("Mikunyan"); VALUE mDecodeHelper = rb_define_module_under(mMikunyan, "DecodeHelper"); VALUE cCrunchStream = rb_define_class_under(mDecodeHelper, "CrunchStream", rb_cObject); rb_attr(cCrunchStream, rb_intern("data"), 1, 0, 1); const char* stFileInfoStr[] = {"struct_size", "actual_data_size", "header_size", "total_palette_size", "tables_size", "levels", "level_compressed_size", "color_endpoint_palette_entries", "color_selector_palette_entries", "alpha_endpoint_palette_entries", "alpha_selector_palette_entries"}; stFileInfo = create_rb_struct(sizeof(stFileInfoStr) / sizeof(char*), stFileInfoStr); rb_const_set(cCrunchStream, rb_intern("FileInfo"), stFileInfo); const char* stTextureInfoStr[] = {"struct_size", "width", "height", "levels", "faces", "bytes_per_block", "userdata0", "userdata1", "format"}; stTextureInfo = create_rb_struct(sizeof(stTextureInfoStr) / sizeof(char*), stTextureInfoStr); rb_const_set(cCrunchStream, rb_intern("TextureInfo"), stTextureInfo); const char* stLevelInfoStr[] = {"struct_size", "width", "height", "faces", "blocks_x", "blocks_y", "block_width", "block_height", "bytes_per_block", "format"}; stLevelInfo = create_rb_struct(sizeof(stLevelInfoStr) / sizeof(char*), stLevelInfoStr); rb_const_set(cCrunchStream, rb_intern("LevelInfo"), stLevelInfo); rb_define_method(cCrunchStream, "initialize", RUBY_METHOD_FUNC(rb_cCrunchStream_initialize), 1); rb_define_method(cCrunchStream, "file_info", RUBY_METHOD_FUNC(rb_cCrunchStream_file_info), 0); rb_define_method(cCrunchStream, "texture_info", RUBY_METHOD_FUNC(rb_cCrunchStream_texture_info), 0); rb_define_method(cCrunchStream, "level_info", RUBY_METHOD_FUNC(rb_cCrunchStream_level_info), 1); rb_define_method(cCrunchStream, "unpack_level", RUBY_METHOD_FUNC(rb_cCrunchStream_unpack_level), 1); VALUE mFormat = rb_define_module_under(cCrunchStream, "Format"); set_format_constant(mFormat); } }