Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, const String &p_path, Vector<SharedObject> *p_so_files) { EditorProgress ep("savepack", TTR("Packing"), 102, true); String tmppath = EditorSettings::get_singleton()->get_cache_dir().plus_file("packtmp"); FileAccess *ftmp = FileAccess::open(tmppath, FileAccess::WRITE); ERR_FAIL_COND_V(!ftmp, ERR_CANT_CREATE) PackData pd; pd.ep = &ep; pd.f = ftmp; pd.so_files = p_so_files; Error err = export_project_files(p_preset, _save_pack_file, &pd, _add_shared_object); memdelete(ftmp); //close tmp file if (err) return err; pd.file_ofs.sort(); //do sort, so we can do binary search later FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE); ERR_FAIL_COND_V(!f, ERR_CANT_CREATE) f->store_32(0x43504447); //GDPK f->store_32(1); //pack version f->store_32(VERSION_MAJOR); f->store_32(VERSION_MINOR); f->store_32(0); //hmph for (int i = 0; i < 16; i++) { //reserved f->store_32(0); } f->store_32(pd.file_ofs.size()); //amount of files size_t header_size = f->get_position(); //precalculate header size for (int i = 0; i < pd.file_ofs.size(); i++) { header_size += 4; // size of path string (32 bits is enough) uint32_t string_len = pd.file_ofs[i].path_utf8.length(); header_size += string_len + _get_pad(4, string_len); ///size of path string header_size += 8; // offset to file _with_ header size included header_size += 8; // size of file header_size += 16; // md5 } size_t header_padding = _get_pad(PCK_PADDING, header_size); for (int i = 0; i < pd.file_ofs.size(); i++) { uint32_t string_len = pd.file_ofs[i].path_utf8.length(); uint32_t pad = _get_pad(4, string_len); ; f->store_32(string_len + pad); f->store_buffer((const uint8_t *)pd.file_ofs[i].path_utf8.get_data(), string_len); for (uint32_t j = 0; j < pad; j++) { f->store_8(0); } f->store_64(pd.file_ofs[i].ofs + header_padding + header_size); f->store_64(pd.file_ofs[i].size); // pay attention here, this is where file is f->store_buffer(pd.file_ofs[i].md5.ptr(), 16); //also save md5 for file } for (uint32_t j = 0; j < header_padding; j++) { f->store_8(0); } //save the rest of the data ftmp = FileAccess::open(tmppath, FileAccess::READ); if (!ftmp) { memdelete(f); ERR_FAIL_COND_V(!ftmp, ERR_CANT_CREATE) }
void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String &p_to_path, int p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, int p_texture_flags, bool p_streamable, bool p_detect_3d, bool p_detect_srgb, bool p_force_rgbe, bool p_detect_normal, bool p_force_normal) { print_line("saving: " + p_to_path); FileAccess *f = FileAccess::open(p_to_path, FileAccess::WRITE); f->store_8('G'); f->store_8('D'); f->store_8('S'); f->store_8('T'); //godot streamable texture f->store_32(p_image->get_width()); f->store_32(p_image->get_height()); f->store_32(p_texture_flags); uint32_t format = 0; if (p_streamable) format |= StreamTexture::FORMAT_BIT_STREAM; if (p_mipmaps || p_compress_mode == COMPRESS_VIDEO_RAM) //VRAM always uses mipmaps format |= StreamTexture::FORMAT_BIT_HAS_MIPMAPS; //mipmaps bit if (p_detect_3d) format |= StreamTexture::FORMAT_BIT_DETECT_3D; if (p_detect_srgb) format |= StreamTexture::FORMAT_BIT_DETECT_SRGB; if (p_detect_normal) format |= StreamTexture::FORMAT_BIT_DETECT_NORMAL; if ((p_compress_mode == COMPRESS_LOSSLESS || p_compress_mode == COMPRESS_LOSSY) && p_image->get_format() > Image::FORMAT_RGBA8) { p_compress_mode == COMPRESS_UNCOMPRESSED; //these can't go as lossy } switch (p_compress_mode) { case COMPRESS_LOSSLESS: { Ref<Image> image = p_image->duplicate(); if (p_mipmaps) { image->generate_mipmaps(); } else { image->clear_mipmaps(); } int mmc = image->get_mipmap_count() + 1; format |= StreamTexture::FORMAT_BIT_LOSSLESS; f->store_32(format); f->store_32(mmc); for (int i = 0; i < mmc; i++) { if (i > 0) { image->shrink_x2(); } PoolVector<uint8_t> data = Image::lossless_packer(image); int data_len = data.size(); f->store_32(data_len); PoolVector<uint8_t>::Read r = data.read(); f->store_buffer(r.ptr(), data_len); } } break; case COMPRESS_LOSSY: { Ref<Image> image = p_image->duplicate(); if (p_mipmaps) { image->generate_mipmaps(); } else { image->clear_mipmaps(); } int mmc = image->get_mipmap_count() + 1; format |= StreamTexture::FORMAT_BIT_LOSSY; f->store_32(format); f->store_32(mmc); for (int i = 0; i < mmc; i++) { if (i > 0) { image->shrink_x2(); } PoolVector<uint8_t> data = Image::lossy_packer(image, p_lossy_quality); int data_len = data.size(); f->store_32(data_len); PoolVector<uint8_t>::Read r = data.read(); f->store_buffer(r.ptr(), data_len); } } break; case COMPRESS_VIDEO_RAM: { Ref<Image> image = p_image->duplicate(); image->generate_mipmaps(); if (p_force_rgbe && image->get_format() >= Image::FORMAT_R8 && image->get_format() <= Image::FORMAT_RGBE9995) { image->convert(Image::FORMAT_RGBE9995); } else { Image::CompressSource csource = Image::COMPRESS_SOURCE_GENERIC; if (p_force_normal) { csource = Image::COMPRESS_SOURCE_NORMAL; } else if (p_texture_flags & VS::TEXTURE_FLAG_CONVERT_TO_LINEAR) { csource = Image::COMPRESS_SOURCE_SRGB; } image->compress(p_vram_compression, csource, p_lossy_quality); } format |= image->get_format(); f->store_32(format); PoolVector<uint8_t> data = image->get_data(); int dl = data.size(); PoolVector<uint8_t>::Read r = data.read(); f->store_buffer(r.ptr(), dl); } break; case COMPRESS_UNCOMPRESSED: { Ref<Image> image = p_image->duplicate(); if (p_mipmaps) { image->generate_mipmaps(); } else { image->clear_mipmaps(); } format |= image->get_format(); f->store_32(format); PoolVector<uint8_t> data = image->get_data(); int dl = data.size(); PoolVector<uint8_t>::Read r = data.read(); f->store_buffer(r.ptr(), dl); } break; } memdelete(f); }