2014-02-10 02:10:30 +01:00
/**************************************************************************/
/* resource_saver.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2014-02-10 02:10:30 +01:00
# include "resource_saver.h"
2020-11-07 23:33:38 +01:00
# include "core/config/project_settings.h"
2021-06-11 14:51:48 +02:00
# include "core/io/file_access.h"
2018-09-11 18:13:45 +02:00
# include "core/io/resource_loader.h"
2020-11-07 23:33:38 +01:00
# include "core/object/script_language.h"
2014-02-10 02:10:30 +01:00
2018-06-11 02:59:53 +02:00
Ref < ResourceFormatSaver > ResourceSaver : : saver [ MAX_SAVERS ] ;
2014-02-10 02:10:30 +01:00
int ResourceSaver : : saver_count = 0 ;
bool ResourceSaver : : timestamp_on_save = false ;
2020-04-02 01:20:12 +02:00
ResourceSavedCallback ResourceSaver : : save_callback = nullptr ;
2021-07-23 21:01:18 +02:00
ResourceSaverGetResourceIDForPath ResourceSaver : : save_get_id_for_path = nullptr ;
2014-02-10 02:10:30 +01:00
2022-06-03 01:33:42 +02:00
Error ResourceFormatSaver : : save ( const Ref < Resource > & p_resource , const String & p_path , uint32_t p_flags ) {
2023-01-15 20:33:20 +01:00
Error err = ERR_METHOD_NOT_FOUND ;
GDVIRTUAL_CALL ( _save , p_resource , p_path , p_flags , err ) ;
return err ;
2018-06-11 02:59:53 +02:00
}
2022-12-05 19:01:59 +01:00
Error ResourceFormatSaver : : set_uid ( const String & p_path , ResourceUID : : ID p_uid ) {
Error err = ERR_FILE_UNRECOGNIZED ;
GDVIRTUAL_CALL ( _set_uid , p_path , p_uid , err ) ;
return err ;
}
2022-05-03 01:43:50 +02:00
bool ResourceFormatSaver : : recognize ( const Ref < Resource > & p_resource ) const {
2022-09-29 11:53:28 +02:00
bool success = false ;
2022-10-18 18:47:44 +02:00
GDVIRTUAL_CALL ( _recognize , p_resource , success ) ;
return success ;
2018-06-11 02:59:53 +02:00
}
2022-05-03 01:43:50 +02:00
void ResourceFormatSaver : : get_recognized_extensions ( const Ref < Resource > & p_resource , List < String > * p_extensions ) const {
2021-08-22 03:52:44 +02:00
PackedStringArray exts ;
if ( GDVIRTUAL_CALL ( _get_recognized_extensions , p_resource , exts ) ) {
const String * r = exts . ptr ( ) ;
for ( int i = 0 ; i < exts . size ( ) ; + + i ) {
p_extensions - > push_back ( r [ i ] ) ;
2018-06-11 02:59:53 +02:00
}
}
}
2022-10-19 10:46:51 +02:00
bool ResourceFormatSaver : : recognize_path ( const Ref < Resource > & p_resource , const String & p_path ) const {
bool ret = false ;
if ( GDVIRTUAL_CALL ( _recognize_path , p_resource , p_path , ret ) ) {
return ret ;
}
String extension = p_path . get_extension ( ) ;
List < String > extensions ;
get_recognized_extensions ( p_resource , & extensions ) ;
for ( const String & E : extensions ) {
if ( E . nocasecmp_to ( extension ) = = 0 ) {
return true ;
}
}
return false ;
}
2018-06-11 02:59:53 +02:00
void ResourceFormatSaver : : _bind_methods ( ) {
2022-08-24 12:11:34 +02:00
GDVIRTUAL_BIND ( _save , " resource " , " path " , " flags " ) ;
2022-12-05 19:01:59 +01:00
GDVIRTUAL_BIND ( _set_uid , " path " , " uid " ) ;
2021-08-22 03:52:44 +02:00
GDVIRTUAL_BIND ( _recognize , " resource " ) ;
GDVIRTUAL_BIND ( _get_recognized_extensions , " resource " ) ;
2022-10-19 10:46:51 +02:00
GDVIRTUAL_BIND ( _recognize_path , " resource " , " path " ) ;
2018-06-11 02:59:53 +02:00
}
2022-06-03 01:33:42 +02:00
Error ResourceSaver : : save ( const Ref < Resource > & p_resource , const String & p_path , uint32_t p_flags ) {
String path = p_path ;
if ( path . is_empty ( ) ) {
path = p_resource - > get_path ( ) ;
}
2022-07-30 11:08:40 +02:00
ERR_FAIL_COND_V_MSG ( path . is_empty ( ) , ERR_INVALID_PARAMETER , " Can't save resource to empty path. Provide non-empty path or a Resource with non-empty resource_path. " ) ;
2022-06-03 01:33:42 +02:00
String extension = path . get_extension ( ) ;
2014-02-10 02:10:30 +01:00
Error err = ERR_FILE_UNRECOGNIZED ;
for ( int i = 0 ; i < saver_count ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( ! saver [ i ] - > recognize ( p_resource ) ) {
2014-02-10 02:10:30 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2022-10-19 10:46:51 +02:00
if ( ! saver [ i ] - > recognize_path ( p_resource , path ) ) {
2014-02-10 02:10:30 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
String old_path = p_resource - > get_path ( ) ;
2016-03-09 00:00:52 +01:00
2022-06-03 01:33:42 +02:00
String local_path = ProjectSettings : : get_singleton ( ) - > localize_path ( path ) ;
2014-02-10 02:10:30 +01:00
2022-05-03 01:43:50 +02:00
Ref < Resource > rwcopy = p_resource ;
2020-05-14 16:41:43 +02:00
if ( p_flags & FLAG_CHANGE_PATH ) {
2014-02-10 02:10:30 +01:00
rwcopy - > set_path ( local_path ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2022-06-03 01:33:42 +02:00
err = saver [ i ] - > save ( p_resource , path , p_flags ) ;
2014-02-10 02:10:30 +01:00
if ( err = = OK ) {
# ifdef TOOLS_ENABLED
( ( Resource * ) p_resource . ptr ( ) ) - > set_edited ( false ) ;
if ( timestamp_on_save ) {
2022-06-03 01:33:42 +02:00
uint64_t mt = FileAccess : : get_modified_time ( path ) ;
2014-02-10 02:10:30 +01:00
( ( Resource * ) p_resource . ptr ( ) ) - > set_last_modified_time ( mt ) ;
}
# endif
2020-05-14 16:41:43 +02:00
if ( p_flags & FLAG_CHANGE_PATH ) {
2014-02-10 02:10:30 +01:00
rwcopy - > set_path ( old_path ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2022-06-03 01:33:42 +02:00
if ( save_callback & & path . begins_with ( " res:// " ) ) {
save_callback ( p_resource , path ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return OK ;
}
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
return err ;
}
2022-12-05 19:01:59 +01:00
Error ResourceSaver : : set_uid ( const String & p_path , ResourceUID : : ID p_uid ) {
String path = p_path ;
ERR_FAIL_COND_V_MSG ( path . is_empty ( ) , ERR_INVALID_PARAMETER , " Can't update UID to empty path. Provide non-empty path. " ) ;
Error err = ERR_FILE_UNRECOGNIZED ;
for ( int i = 0 ; i < saver_count ; i + + ) {
err = saver [ i ] - > set_uid ( path , p_uid ) ;
if ( err = = OK ) {
break ;
}
}
return err ;
}
2014-02-10 02:10:30 +01:00
void ResourceSaver : : set_save_callback ( ResourceSavedCallback p_callback ) {
save_callback = p_callback ;
}
2022-05-03 01:43:50 +02:00
void ResourceSaver : : get_recognized_extensions ( const Ref < Resource > & p_resource , List < String > * p_extensions ) {
2014-02-10 02:10:30 +01:00
for ( int i = 0 ; i < saver_count ; i + + ) {
saver [ i ] - > get_recognized_extensions ( p_resource , p_extensions ) ;
}
}
2018-06-11 02:59:53 +02:00
void ResourceSaver : : add_resource_format_saver ( Ref < ResourceFormatSaver > p_format_saver , bool p_at_front ) {
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_MSG ( p_format_saver . is_null ( ) , " It's not a reference to a valid ResourceFormatSaver object. " ) ;
2014-02-10 02:10:30 +01:00
ERR_FAIL_COND ( saver_count > = MAX_SAVERS ) ;
2016-07-20 02:40:05 +02:00
if ( p_at_front ) {
for ( int i = saver_count ; i > 0 ; i - - ) {
saver [ i ] = saver [ i - 1 ] ;
}
saver [ 0 ] = p_format_saver ;
saver_count + + ;
} else {
saver [ saver_count + + ] = p_format_saver ;
}
2014-02-10 02:10:30 +01:00
}
2018-06-11 02:59:53 +02:00
void ResourceSaver : : remove_resource_format_saver ( Ref < ResourceFormatSaver > p_format_saver ) {
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_MSG ( p_format_saver . is_null ( ) , " It's not a reference to a valid ResourceFormatSaver object. " ) ;
2018-06-11 02:59:53 +02:00
// Find saver
int i = 0 ;
for ( ; i < saver_count ; + + i ) {
2020-05-14 16:41:43 +02:00
if ( saver [ i ] = = p_format_saver ) {
2018-06-11 02:59:53 +02:00
break ;
2020-05-14 16:41:43 +02:00
}
2018-06-11 02:59:53 +02:00
}
ERR_FAIL_COND ( i > = saver_count ) ; // Not found
// Shift next savers up
for ( ; i < saver_count - 1 ; + + i ) {
saver [ i ] = saver [ i + 1 ] ;
}
saver [ saver_count - 1 ] . unref ( ) ;
- - saver_count ;
}
Ref < ResourceFormatSaver > ResourceSaver : : _find_custom_resource_format_saver ( String path ) {
for ( int i = 0 ; i < saver_count ; + + i ) {
if ( saver [ i ] - > get_script_instance ( ) & & saver [ i ] - > get_script_instance ( ) - > get_script ( ) - > get_path ( ) = = path ) {
return saver [ i ] ;
}
}
return Ref < ResourceFormatSaver > ( ) ;
}
bool ResourceSaver : : add_custom_resource_format_saver ( String script_path ) {
2020-05-14 16:41:43 +02:00
if ( _find_custom_resource_format_saver ( script_path ) . is_valid ( ) ) {
2018-06-11 02:59:53 +02:00
return false ;
2020-05-14 16:41:43 +02:00
}
2018-06-11 02:59:53 +02:00
Ref < Resource > res = ResourceLoader : : load ( script_path ) ;
ERR_FAIL_COND_V ( res . is_null ( ) , false ) ;
ERR_FAIL_COND_V ( ! res - > is_class ( " Script " ) , false ) ;
Ref < Script > s = res ;
StringName ibt = s - > get_instance_base_type ( ) ;
bool valid_type = ClassDB : : is_parent_class ( ibt , " ResourceFormatSaver " ) ;
2023-11-23 16:19:24 +01:00
ERR_FAIL_COND_V_MSG ( ! valid_type , false , vformat ( " Failed to add a custom resource saver, script '%s' does not inherit 'ResourceFormatSaver'. " , script_path ) ) ;
2018-06-11 02:59:53 +02:00
2021-06-18 00:03:09 +02:00
Object * obj = ClassDB : : instantiate ( ibt ) ;
2023-11-23 16:19:24 +01:00
ERR_FAIL_NULL_V_MSG ( obj , false , vformat ( " Failed to add a custom resource saver, cannot instantiate '%s'. " , ibt ) ) ;
2018-06-11 02:59:53 +02:00
2020-11-15 15:14:06 +01:00
Ref < ResourceFormatSaver > crl = Object : : cast_to < ResourceFormatSaver > ( obj ) ;
2020-02-13 20:03:10 +01:00
crl - > set_script ( s ) ;
2018-06-11 02:59:53 +02:00
ResourceSaver : : add_resource_format_saver ( crl ) ;
return true ;
}
void ResourceSaver : : add_custom_savers ( ) {
// Custom resource savers exploits global class names
String custom_saver_base_class = ResourceFormatSaver : : get_class_static ( ) ;
List < StringName > global_classes ;
ScriptServer : : get_global_class_list ( & global_classes ) ;
2021-07-24 15:46:25 +02:00
for ( const StringName & class_name : global_classes ) {
2019-03-09 04:47:27 +01:00
StringName base_class = ScriptServer : : get_global_class_native_base ( class_name ) ;
2018-06-11 02:59:53 +02:00
if ( base_class = = custom_saver_base_class ) {
String path = ScriptServer : : get_global_class_path ( class_name ) ;
add_custom_resource_format_saver ( path ) ;
}
}
}
void ResourceSaver : : remove_custom_savers ( ) {
2020-03-17 07:33:00 +01:00
Vector < Ref < ResourceFormatSaver > > custom_savers ;
2018-06-11 02:59:53 +02:00
for ( int i = 0 ; i < saver_count ; + + i ) {
if ( saver [ i ] - > get_script_instance ( ) ) {
custom_savers . push_back ( saver [ i ] ) ;
}
}
for ( int i = 0 ; i < custom_savers . size ( ) ; + + i ) {
remove_resource_format_saver ( custom_savers [ i ] ) ;
}
}
2021-07-23 21:01:18 +02:00
ResourceUID : : ID ResourceSaver : : get_resource_id_for_path ( const String & p_path , bool p_generate ) {
if ( save_get_id_for_path ) {
return save_get_id_for_path ( p_path , p_generate ) ;
}
return ResourceUID : : INVALID_ID ;
}
void ResourceSaver : : set_get_resource_id_for_path ( ResourceSaverGetResourceIDForPath p_callback ) {
save_get_id_for_path = p_callback ;
}