2017-04-28 20:04:09 +02:00
/**************************************************************************/
/* export_template_manager.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
2017-03-21 03:31:41 +01:00
# include "export_template_manager.h"
2017-04-28 20:04:09 +02:00
2021-06-11 14:51:48 +02:00
# include "core/io/dir_access.h"
2018-09-11 18:13:45 +02:00
# include "core/io/json.h"
# include "core/io/zip_io.h"
# include "core/version.h"
2022-02-12 02:46:22 +01:00
# include "editor/editor_node.h"
2022-02-14 14:00:03 +01:00
# include "editor/editor_paths.h"
2022-07-31 20:14:15 +02:00
# include "editor/editor_settings.h"
2023-08-13 02:33:39 +02:00
# include "editor/editor_string_names.h"
2024-02-13 21:20:23 +01:00
# include "editor/export/editor_export.h"
2022-07-21 00:45:01 +02:00
# include "editor/progress_dialog.h"
2024-01-15 13:14:55 +01:00
# include "editor/themes/editor_scale.h"
2022-07-21 00:45:01 +02:00
# include "scene/gui/file_dialog.h"
2022-11-19 12:45:49 +01:00
# include "scene/gui/menu_button.h"
2022-07-31 20:14:15 +02:00
# include "scene/gui/separator.h"
2022-07-21 00:45:01 +02:00
# include "scene/gui/tree.h"
# include "scene/main/http_request.h"
2017-11-17 21:48:24 +01:00
2024-08-30 16:14:31 +02:00
enum DownloadsAvailability {
DOWNLOADS_AVAILABLE ,
DOWNLOADS_NOT_AVAILABLE_IN_OFFLINE_MODE ,
DOWNLOADS_NOT_AVAILABLE_FOR_DEV_BUILDS ,
} ;
static DownloadsAvailability _get_downloads_availability ( ) {
const int network_mode = EDITOR_GET ( " network/connection/network_mode " ) ;
if ( network_mode = = EditorSettings : : NETWORK_OFFLINE ) {
return DOWNLOADS_NOT_AVAILABLE_IN_OFFLINE_MODE ;
}
// Downloadable export templates are only available for stable and official alpha/beta/RC builds
// (which always have a number following their status, e.g. "alpha1").
// Therefore, don't display download-related features when using a development version
// (whose builds aren't numbered).
if ( String ( VERSION_STATUS ) = = String ( " dev " ) | |
String ( VERSION_STATUS ) = = String ( " alpha " ) | |
String ( VERSION_STATUS ) = = String ( " beta " ) | |
String ( VERSION_STATUS ) = = String ( " rc " ) ) {
return DOWNLOADS_NOT_AVAILABLE_FOR_DEV_BUILDS ;
}
return DOWNLOADS_AVAILABLE ;
}
2021-05-20 22:15:49 +02:00
void ExportTemplateManager : : _update_template_status ( ) {
// Fetch installed templates from the file system.
2022-03-23 10:08:58 +01:00
Ref < DirAccess > da = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
2022-07-29 02:36:26 +02:00
const String & templates_dir = EditorPaths : : get_singleton ( ) - > get_export_templates_dir ( ) ;
2017-03-21 03:31:41 +01:00
2021-05-20 22:15:49 +02:00
Error err = da - > change_dir ( templates_dir ) ;
ERR_FAIL_COND_MSG ( err ! = OK , " Could not access templates directory at ' " + templates_dir + " '. " ) ;
2017-03-21 03:31:41 +01:00
2022-05-13 15:04:37 +02:00
RBSet < String > templates ;
2021-05-20 22:15:49 +02:00
da - > list_dir_begin ( ) ;
2017-03-21 03:31:41 +01:00
if ( err = = OK ) {
2021-05-20 22:15:49 +02:00
String c = da - > get_next ( ) ;
2021-12-09 10:42:46 +01:00
while ( ! c . is_empty ( ) ) {
2021-05-20 22:15:49 +02:00
if ( da - > current_is_dir ( ) & & ! c . begins_with ( " . " ) ) {
2017-03-21 03:31:41 +01:00
templates . insert ( c ) ;
}
2021-05-20 22:15:49 +02:00
c = da - > get_next ( ) ;
2017-03-21 03:31:41 +01:00
}
}
2021-05-20 22:15:49 +02:00
da - > list_dir_end ( ) ;
2017-03-21 03:31:41 +01:00
2021-05-20 22:15:49 +02:00
// Update the state of the current version.
Refactor version macros and fix related bugs
The previous logic with VERSION_MKSTRING was a bit unwieldy, so there were
several places hardcoding their own variant of the version string, potentially
with bugs (e.g. forgetting the patch number when defined).
The new logic defines:
- VERSION_BRANCH, the main 'major.minor' version (e.g. 3.1)
- VERSION_NUMBER, which can be 'major.minor' or 'major.minor.patch',
depending on whether the latter is defined (e.g. 3.1.4)
- VERSION_FULL_CONFIG, which contains the version status (e.g. stable)
and the module-specific suffix (e.g. mono)
- VERSION_FULL_BUILD, same as above but with build/reference name
(e.g. official, custom_build, mageia, etc.)
Note: Slight change here, as the previous format had the build name
*before* the module-specific suffix; now it's after
- VERSION_FULL_NAME, same as before, so VERSION_FULL_BUILD prefixed
with "Godot v" for readability
Bugs fixed thanks to that:
- Export templates version matching now properly takes VERSION_PATCH
into account by relying on VERSION_FULL_CONFIG.
- ClassDB hash no longer takes the build name into account, but limits
itself to VERSION_FULL_CONFIG (build name is cosmetic, not relevant
for the API hash).
- Docs XML no longer hardcode the VERSION_STATUS, this was annoying.
- Small cleanup in Windows .rc file thanks to new macros.
2018-02-23 19:48:49 +01:00
String current_version = VERSION_FULL_CONFIG ;
2021-05-20 22:15:49 +02:00
current_value - > set_text ( current_version ) ;
2017-03-21 03:31:41 +01:00
if ( templates . has ( current_version ) ) {
2021-05-20 22:15:49 +02:00
current_missing_label - > hide ( ) ;
current_installed_label - > show ( ) ;
current_installed_hb - > show ( ) ;
current_version_exists = true ;
} else {
current_installed_label - > hide ( ) ;
current_missing_label - > show ( ) ;
2017-03-21 03:31:41 +01:00
2021-05-20 22:15:49 +02:00
current_installed_hb - > hide ( ) ;
current_version_exists = false ;
}
2017-03-21 03:31:41 +01:00
2021-05-20 22:15:49 +02:00
if ( is_downloading_templates ) {
install_options_vb - > hide ( ) ;
download_progress_hb - > show ( ) ;
2017-03-21 03:31:41 +01:00
} else {
2021-05-20 22:15:49 +02:00
download_progress_hb - > hide ( ) ;
install_options_vb - > show ( ) ;
2019-08-12 17:05:25 +02:00
2021-05-20 22:15:49 +02:00
if ( templates . has ( current_version ) ) {
2022-08-30 02:34:01 +02:00
current_installed_path - > set_text ( templates_dir . path_join ( current_version ) ) ;
2019-08-12 17:05:25 +02:00
}
2017-03-21 03:31:41 +01:00
}
2021-05-20 22:15:49 +02:00
// Update the list of other installed versions.
installed_table - > clear ( ) ;
TreeItem * installed_root = installed_table - > create_item ( ) ;
2022-05-13 15:04:37 +02:00
for ( RBSet < String > : : Element * E = templates . back ( ) ; E ; E = E - > prev ( ) ) {
2021-05-20 22:15:49 +02:00
String version_string = E - > get ( ) ;
if ( version_string = = current_version ) {
2021-03-13 00:32:53 +01:00
continue ;
2017-03-21 03:31:41 +01:00
}
2021-03-13 00:32:53 +01:00
2021-05-20 22:15:49 +02:00
TreeItem * ti = installed_table - > create_item ( installed_root ) ;
ti - > set_text ( 0 , version_string ) ;
2024-06-16 21:14:34 +02:00
# ifndef ANDROID_ENABLED
2023-08-13 02:33:39 +02:00
ti - > add_button ( 0 , get_editor_theme_icon ( SNAME ( " Folder " ) ) , OPEN_TEMPLATE_FOLDER , false , TTR ( " Open the folder containing these templates. " ) ) ;
2024-06-16 21:14:34 +02:00
# endif
2023-08-13 02:33:39 +02:00
ti - > add_button ( 0 , get_editor_theme_icon ( SNAME ( " Remove " ) ) , UNINSTALL_TEMPLATE , false , TTR ( " Uninstall these templates. " ) ) ;
2021-05-20 22:15:49 +02:00
}
}
void ExportTemplateManager : : _download_current ( ) {
if ( is_downloading_templates ) {
return ;
}
is_downloading_templates = true ;
2017-03-21 03:31:41 +01:00
2021-05-20 22:15:49 +02:00
install_options_vb - > hide ( ) ;
download_progress_hb - > show ( ) ;
2017-03-21 03:31:41 +01:00
2021-05-20 22:15:49 +02:00
if ( mirrors_available ) {
String mirror_url = _get_selected_mirror ( ) ;
if ( mirror_url . is_empty ( ) ) {
_set_current_progress_status ( TTR ( " There are no mirrors available. " ) , true ) ;
return ;
}
2017-03-21 03:31:41 +01:00
2021-05-20 22:15:49 +02:00
_download_template ( mirror_url , true ) ;
2022-04-07 12:23:40 +02:00
} else if ( ! is_refreshing_mirrors ) {
2021-05-20 22:15:49 +02:00
_set_current_progress_status ( TTR ( " Retrieving the mirror list... " ) ) ;
_refresh_mirrors ( ) ;
2017-03-21 03:31:41 +01:00
}
}
2021-05-20 22:15:49 +02:00
void ExportTemplateManager : : _download_template ( const String & p_url , bool p_skip_check ) {
if ( ! p_skip_check & & is_downloading_templates ) {
return ;
}
is_downloading_templates = true ;
install_options_vb - > hide ( ) ;
download_progress_hb - > show ( ) ;
2024-02-08 01:16:52 +01:00
download_progress_bar - > show ( ) ;
download_progress_bar - > set_indeterminate ( true ) ;
2021-05-20 22:15:49 +02:00
_set_current_progress_status ( TTR ( " Starting the download... " ) ) ;
2022-08-30 02:34:01 +02:00
download_templates - > set_download_file ( EditorPaths : : get_singleton ( ) - > get_cache_dir ( ) . path_join ( " tmp_templates.tpz " ) ) ;
2021-05-20 22:15:49 +02:00
download_templates - > set_use_threads ( true ) ;
2022-03-20 13:56:11 +01:00
const String proxy_host = EDITOR_GET ( " network/http_proxy/host " ) ;
const int proxy_port = EDITOR_GET ( " network/http_proxy/port " ) ;
2021-12-09 04:34:32 +01:00
download_templates - > set_http_proxy ( proxy_host , proxy_port ) ;
download_templates - > set_https_proxy ( proxy_host , proxy_port ) ;
2021-05-20 22:15:49 +02:00
Error err = download_templates - > request ( p_url ) ;
if ( err ! = OK ) {
_set_current_progress_status ( TTR ( " Error requesting URL: " ) + " " + p_url , true ) ;
2024-02-08 01:16:52 +01:00
download_progress_hb - > hide ( ) ;
2021-05-20 22:15:49 +02:00
return ;
}
set_process ( true ) ;
_set_current_progress_status ( TTR ( " Connecting to the mirror... " ) ) ;
2017-03-21 03:31:41 +01:00
}
2021-05-20 22:15:49 +02:00
void ExportTemplateManager : : _download_template_completed ( int p_status , int p_code , const PackedStringArray & headers , const PackedByteArray & p_data ) {
switch ( p_status ) {
case HTTPRequest : : RESULT_CANT_RESOLVE : {
_set_current_progress_status ( TTR ( " Can't resolve the requested address. " ) , true ) ;
} break ;
case HTTPRequest : : RESULT_BODY_SIZE_LIMIT_EXCEEDED :
case HTTPRequest : : RESULT_CONNECTION_ERROR :
case HTTPRequest : : RESULT_CHUNKED_BODY_SIZE_MISMATCH :
2022-09-07 08:25:47 +02:00
case HTTPRequest : : RESULT_TLS_HANDSHAKE_ERROR :
2021-05-20 22:15:49 +02:00
case HTTPRequest : : RESULT_CANT_CONNECT : {
_set_current_progress_status ( TTR ( " Can't connect to the mirror. " ) , true ) ;
} break ;
case HTTPRequest : : RESULT_NO_RESPONSE : {
_set_current_progress_status ( TTR ( " No response from the mirror. " ) , true ) ;
} break ;
case HTTPRequest : : RESULT_REQUEST_FAILED : {
_set_current_progress_status ( TTR ( " Request failed. " ) , true ) ;
} break ;
case HTTPRequest : : RESULT_REDIRECT_LIMIT_REACHED : {
_set_current_progress_status ( TTR ( " Request ended up in a redirect loop. " ) , true ) ;
} break ;
default : {
if ( p_code ! = 200 ) {
_set_current_progress_status ( TTR ( " Request failed: " ) + " " + itos ( p_code ) , true ) ;
} else {
_set_current_progress_status ( TTR ( " Download complete; extracting templates... " ) ) ;
String path = download_templates - > get_download_file ( ) ;
is_downloading_templates = false ;
bool ret = _install_file_selected ( path , true ) ;
if ( ret ) {
// Clean up downloaded file.
2022-03-23 10:08:58 +01:00
Ref < DirAccess > da = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
2021-05-20 22:15:49 +02:00
Error err = da - > remove ( path ) ;
if ( err ! = OK ) {
EditorNode : : get_singleton ( ) - > add_io_error ( TTR ( " Cannot remove temporary file: " ) + " \n " + path + " \n " ) ;
}
} else {
EditorNode : : get_singleton ( ) - > add_io_error ( vformat ( TTR ( " Templates installation failed. \n The problematic templates archives can be found at '%s'. " ) , path ) ) ;
}
}
} break ;
}
set_process ( false ) ;
2017-03-21 03:31:41 +01:00
}
2021-05-20 22:15:49 +02:00
void ExportTemplateManager : : _cancel_template_download ( ) {
if ( ! is_downloading_templates ) {
return ;
}
2017-03-21 03:31:41 +01:00
2021-05-20 22:15:49 +02:00
download_templates - > cancel_request ( ) ;
download_progress_hb - > hide ( ) ;
install_options_vb - > show ( ) ;
is_downloading_templates = false ;
}
2017-03-21 03:31:41 +01:00
2021-05-20 22:15:49 +02:00
void ExportTemplateManager : : _refresh_mirrors ( ) {
if ( is_refreshing_mirrors ) {
return ;
}
is_refreshing_mirrors = true ;
String current_version = VERSION_FULL_CONFIG ;
const String mirrors_metadata_url = " https://godotengine.org/mirrorlist/ " + current_version + " .json " ;
request_mirrors - > request ( mirrors_metadata_url ) ;
}
void ExportTemplateManager : : _refresh_mirrors_completed ( int p_status , int p_code , const PackedStringArray & headers , const PackedByteArray & p_data ) {
if ( p_status ! = HTTPRequest : : RESULT_SUCCESS | | p_code ! = 200 ) {
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " Error getting the list of mirrors. " ) ) ;
is_refreshing_mirrors = false ;
if ( is_downloading_templates ) {
_cancel_template_download ( ) ;
}
return ;
}
String response_json ;
{
const uint8_t * r = p_data . ptr ( ) ;
response_json . parse_utf8 ( ( const char * ) r , p_data . size ( ) ) ;
}
2020-12-29 19:12:33 +01:00
JSON json ;
Error err = json . parse ( response_json ) ;
2021-05-20 22:15:49 +02:00
if ( err ! = OK ) {
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " Error parsing JSON with the list of mirrors. Please report this issue! " ) ) ;
is_refreshing_mirrors = false ;
if ( is_downloading_templates ) {
_cancel_template_download ( ) ;
}
return ;
}
mirrors_list - > clear ( ) ;
mirrors_list - > add_item ( TTR ( " Best available mirror " ) , 0 ) ;
mirrors_available = false ;
2022-09-29 11:53:28 +02:00
Dictionary mirror_data = json . get_data ( ) ;
if ( mirror_data . has ( " mirrors " ) ) {
Array mirrors = mirror_data [ " mirrors " ] ;
2021-05-20 22:15:49 +02:00
for ( int i = 0 ; i < mirrors . size ( ) ; i + + ) {
Dictionary m = mirrors [ i ] ;
ERR_CONTINUE ( ! m . has ( " url " ) | | ! m . has ( " name " ) ) ;
mirrors_list - > add_item ( m [ " name " ] ) ;
mirrors_list - > set_item_metadata ( i + 1 , m [ " url " ] ) ;
mirrors_available = true ;
}
}
if ( ! mirrors_available ) {
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " No download links found for this version. Direct download is only available for official releases. " ) ) ;
if ( is_downloading_templates ) {
_cancel_template_download ( ) ;
}
}
is_refreshing_mirrors = false ;
if ( is_downloading_templates ) {
String mirror_url = _get_selected_mirror ( ) ;
if ( mirror_url . is_empty ( ) ) {
_set_current_progress_status ( TTR ( " There are no mirrors available. " ) , true ) ;
return ;
}
_download_template ( mirror_url , true ) ;
}
}
bool ExportTemplateManager : : _humanize_http_status ( HTTPRequest * p_request , String * r_status , int * r_downloaded_bytes , int * r_total_bytes ) {
* r_status = " " ;
* r_downloaded_bytes = - 1 ;
* r_total_bytes = - 1 ;
bool success = true ;
switch ( p_request - > get_http_client_status ( ) ) {
case HTTPClient : : STATUS_DISCONNECTED :
* r_status = TTR ( " Disconnected " ) ;
success = false ;
break ;
case HTTPClient : : STATUS_RESOLVING :
* r_status = TTR ( " Resolving " ) ;
break ;
case HTTPClient : : STATUS_CANT_RESOLVE :
* r_status = TTR ( " Can't Resolve " ) ;
success = false ;
break ;
case HTTPClient : : STATUS_CONNECTING :
* r_status = TTR ( " Connecting... " ) ;
break ;
case HTTPClient : : STATUS_CANT_CONNECT :
* r_status = TTR ( " Can't Connect " ) ;
success = false ;
break ;
case HTTPClient : : STATUS_CONNECTED :
* r_status = TTR ( " Connected " ) ;
break ;
case HTTPClient : : STATUS_REQUESTING :
* r_status = TTR ( " Requesting... " ) ;
break ;
case HTTPClient : : STATUS_BODY :
* r_status = TTR ( " Downloading " ) ;
* r_downloaded_bytes = p_request - > get_downloaded_bytes ( ) ;
* r_total_bytes = p_request - > get_body_size ( ) ;
if ( p_request - > get_body_size ( ) > 0 ) {
* r_status + = " " + String : : humanize_size ( p_request - > get_downloaded_bytes ( ) ) + " / " + String : : humanize_size ( p_request - > get_body_size ( ) ) ;
} else {
* r_status + = " " + String : : humanize_size ( p_request - > get_downloaded_bytes ( ) ) ;
}
break ;
case HTTPClient : : STATUS_CONNECTION_ERROR :
* r_status = TTR ( " Connection Error " ) ;
success = false ;
break ;
2022-09-07 08:25:47 +02:00
case HTTPClient : : STATUS_TLS_HANDSHAKE_ERROR :
* r_status = TTR ( " TLS Handshake Error " ) ;
2021-05-20 22:15:49 +02:00
success = false ;
break ;
}
return success ;
}
void ExportTemplateManager : : _set_current_progress_status ( const String & p_status , bool p_error ) {
download_progress_label - > set_text ( p_status ) ;
if ( p_error ) {
2024-02-08 01:16:52 +01:00
download_progress_bar - > hide ( ) ;
2024-05-14 15:57:29 +02:00
download_progress_label - > add_theme_color_override ( SceneStringName ( font_color ) , get_theme_color ( SNAME ( " error_color " ) , EditorStringName ( Editor ) ) ) ;
2021-05-20 22:15:49 +02:00
} else {
2024-05-14 15:57:29 +02:00
download_progress_label - > add_theme_color_override ( SceneStringName ( font_color ) , get_theme_color ( SceneStringName ( font_color ) , SNAME ( " Label " ) ) ) ;
2021-05-20 22:15:49 +02:00
}
}
void ExportTemplateManager : : _set_current_progress_value ( float p_value , const String & p_status ) {
download_progress_bar - > show ( ) ;
2024-02-08 01:16:52 +01:00
download_progress_bar - > set_indeterminate ( false ) ;
2021-05-20 22:15:49 +02:00
download_progress_bar - > set_value ( p_value ) ;
download_progress_label - > set_text ( p_status ) ;
}
2017-03-21 03:31:41 +01:00
2021-05-20 22:15:49 +02:00
void ExportTemplateManager : : _install_file ( ) {
install_file_dialog - > popup_file_dialog ( ) ;
2017-03-21 03:31:41 +01:00
}
2021-05-20 22:15:49 +02:00
bool ExportTemplateManager : : _install_file_selected ( const String & p_file , bool p_skip_progress ) {
2022-05-11 14:15:58 +02:00
Ref < FileAccess > io_fa ;
zlib_filefunc_def io = zipio_create_io ( & io_fa ) ;
2017-03-21 03:31:41 +01:00
unzFile pkg = unzOpen2 ( p_file . utf8 ( ) . get_data ( ) , & io ) ;
if ( ! pkg ) {
2021-05-20 22:15:49 +02:00
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " Can't open the export templates file. " ) ) ;
2018-08-13 12:46:02 +02:00
return false ;
2017-03-21 03:31:41 +01:00
}
int ret = unzGoToFirstFile ( pkg ) ;
2021-05-20 22:15:49 +02:00
// Count them and find version.
int fc = 0 ;
2017-03-21 03:31:41 +01:00
String version ;
2019-02-25 19:37:51 +01:00
String contents_dir ;
2017-03-21 03:31:41 +01:00
while ( ret = = UNZ_OK ) {
unz_file_info info ;
char fname [ 16384 ] ;
2020-04-02 01:20:12 +02:00
ret = unzGetCurrentFileInfo ( pkg , & info , fname , 16384 , nullptr , 0 , nullptr , 0 ) ;
2022-04-05 12:40:26 +02:00
if ( ret ! = UNZ_OK ) {
break ;
}
2017-03-21 03:31:41 +01:00
2022-01-05 13:27:11 +01:00
String file = String : : utf8 ( fname ) ;
2017-03-21 03:31:41 +01:00
if ( file . ends_with ( " version.txt " ) ) {
2022-09-29 11:53:28 +02:00
Vector < uint8_t > uncomp_data ;
uncomp_data . resize ( info . uncompressed_size ) ;
2017-03-21 03:31:41 +01:00
2021-05-20 22:15:49 +02:00
// Read.
2017-08-21 21:15:36 +02:00
unzOpenCurrentFile ( pkg ) ;
2022-09-29 11:53:28 +02:00
ret = unzReadCurrentFile ( pkg , uncomp_data . ptrw ( ) , uncomp_data . size ( ) ) ;
2022-04-16 23:50:48 +02:00
ERR_BREAK_MSG ( ret < 0 , vformat ( " An error occurred while attempting to read from file: %s. This file will not be used. " , file ) ) ;
2017-03-21 03:31:41 +01:00
unzCloseCurrentFile ( pkg ) ;
String data_str ;
2022-09-29 11:53:28 +02:00
data_str . parse_utf8 ( ( const char * ) uncomp_data . ptr ( ) , uncomp_data . size ( ) ) ;
2017-03-21 03:31:41 +01:00
data_str = data_str . strip_edges ( ) ;
2018-02-25 15:24:33 +01:00
// Version number should be of the form major.minor[.patch].status[.module_config]
// so it can in theory have 3 or more slices.
if ( data_str . get_slice_count ( " . " ) < 3 ) {
2021-05-20 22:15:49 +02:00
EditorNode : : get_singleton ( ) - > show_warning ( vformat ( TTR ( " Invalid version.txt format inside the export templates file: %s. " ) , data_str ) ) ;
2017-03-21 03:31:41 +01:00
unzClose ( pkg ) ;
2018-08-13 12:46:02 +02:00
return false ;
2017-03-21 03:31:41 +01:00
}
2018-02-25 15:24:33 +01:00
version = data_str ;
2019-02-25 19:37:51 +01:00
contents_dir = file . get_base_dir ( ) . trim_suffix ( " / " ) . trim_suffix ( " \\ " ) ;
2017-03-21 03:31:41 +01:00
}
2018-07-16 09:05:45 +02:00
if ( file . get_file ( ) . size ( ) ! = 0 ) {
fc + + ;
}
2017-03-21 03:31:41 +01:00
ret = unzGoToNextFile ( pkg ) ;
}
2021-12-09 10:42:46 +01:00
if ( version . is_empty ( ) ) {
2021-05-20 22:15:49 +02:00
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " No version.txt found inside the export templates file. " ) ) ;
2017-03-21 03:31:41 +01:00
unzClose ( pkg ) ;
2018-08-13 12:46:02 +02:00
return false ;
2017-03-21 03:31:41 +01:00
}
2022-03-23 10:08:58 +01:00
Ref < DirAccess > d = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
2022-08-30 02:34:01 +02:00
String template_path = EditorPaths : : get_singleton ( ) - > get_export_templates_dir ( ) . path_join ( version ) ;
2017-03-21 03:31:41 +01:00
Error err = d - > make_dir_recursive ( template_path ) ;
if ( err ! = OK ) {
2021-05-20 22:15:49 +02:00
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " Error creating path for extracting templates: " ) + " \n " + template_path ) ;
2017-03-21 03:31:41 +01:00
unzClose ( pkg ) ;
2018-08-13 12:46:02 +02:00
return false ;
2017-03-21 03:31:41 +01:00
}
2020-04-02 01:20:12 +02:00
EditorProgress * p = nullptr ;
2021-05-20 22:15:49 +02:00
if ( ! p_skip_progress ) {
2017-12-17 19:47:50 +01:00
p = memnew ( EditorProgress ( " ltask " , TTR ( " Extracting Export Templates " ) , fc ) ) ;
}
2017-03-21 03:31:41 +01:00
fc = 0 ;
2021-05-20 22:15:49 +02:00
ret = unzGoToFirstFile ( pkg ) ;
2017-03-21 03:31:41 +01:00
while ( ret = = UNZ_OK ) {
2021-05-20 22:15:49 +02:00
// Get filename.
2017-03-21 03:31:41 +01:00
unz_file_info info ;
char fname [ 16384 ] ;
2022-04-05 12:40:26 +02:00
ret = unzGetCurrentFileInfo ( pkg , & info , fname , 16384 , nullptr , 0 , nullptr , 0 ) ;
if ( ret ! = UNZ_OK ) {
break ;
}
2017-03-21 03:31:41 +01:00
2023-07-12 14:36:12 +02:00
if ( String : : utf8 ( fname ) . ends_with ( " / " ) ) {
// File is a directory, ignore it.
// Directories will be created when extracting each file.
ret = unzGoToNextFile ( pkg ) ;
continue ;
}
2022-01-05 13:27:11 +01:00
String file_path ( String : : utf8 ( fname ) . simplify_path ( ) ) ;
2019-02-25 19:37:51 +01:00
String file = file_path . get_file ( ) ;
2017-03-21 03:31:41 +01:00
2018-07-16 09:05:45 +02:00
if ( file . size ( ) = = 0 ) {
ret = unzGoToNextFile ( pkg ) ;
continue ;
}
2022-09-29 11:53:28 +02:00
Vector < uint8_t > uncomp_data ;
uncomp_data . resize ( info . uncompressed_size ) ;
2017-03-21 03:31:41 +01:00
2021-05-20 22:15:49 +02:00
// Read
2017-08-21 21:15:36 +02:00
unzOpenCurrentFile ( pkg ) ;
2022-09-29 11:53:28 +02:00
ret = unzReadCurrentFile ( pkg , uncomp_data . ptrw ( ) , uncomp_data . size ( ) ) ;
2022-04-16 23:50:48 +02:00
ERR_BREAK_MSG ( ret < 0 , vformat ( " An error occurred while attempting to read from file: %s. This file will not be used. " , file ) ) ;
2017-03-21 03:31:41 +01:00
unzCloseCurrentFile ( pkg ) ;
2019-02-28 00:25:55 +01:00
String base_dir = file_path . get_base_dir ( ) . trim_suffix ( " / " ) ;
2019-02-25 19:37:51 +01:00
if ( base_dir ! = contents_dir & & base_dir . begins_with ( contents_dir ) ) {
2019-02-28 00:25:55 +01:00
base_dir = base_dir . substr ( contents_dir . length ( ) , file_path . length ( ) ) . trim_prefix ( " / " ) ;
2022-08-30 02:34:01 +02:00
file = base_dir . path_join ( file ) ;
2019-02-25 19:37:51 +01:00
2022-03-23 10:08:58 +01:00
Ref < DirAccess > da = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
ERR_CONTINUE ( da . is_null ( ) ) ;
2019-02-25 19:37:51 +01:00
2022-08-30 02:34:01 +02:00
String output_dir = template_path . path_join ( base_dir ) ;
2019-02-25 19:37:51 +01:00
if ( ! DirAccess : : exists ( output_dir ) ) {
Error mkdir_err = da - > make_dir_recursive ( output_dir ) ;
ERR_CONTINUE ( mkdir_err ! = OK ) ;
}
}
2017-12-17 19:47:50 +01:00
if ( p ) {
p - > step ( TTR ( " Importing: " ) + " " + file , fc ) ;
}
2017-03-21 03:31:41 +01:00
2022-08-30 02:34:01 +02:00
String to_write = template_path . path_join ( file ) ;
2022-03-23 10:08:58 +01:00
Ref < FileAccess > f = FileAccess : : open ( to_write , FileAccess : : WRITE ) ;
2017-03-21 03:31:41 +01:00
2022-03-23 10:08:58 +01:00
if ( f . is_null ( ) ) {
2017-12-18 20:44:19 +01:00
ret = unzGoToNextFile ( pkg ) ;
fc + + ;
2019-09-25 10:28:50 +02:00
ERR_CONTINUE_MSG ( true , " Can't open file from path ' " + String ( to_write ) + " '. " ) ;
2017-12-18 20:44:19 +01:00
}
2022-09-29 11:53:28 +02:00
f - > store_buffer ( uncomp_data . ptr ( ) , uncomp_data . size ( ) ) ;
2022-04-12 09:12:40 +02:00
f . unref ( ) ; // close file.
2019-04-07 20:46:52 +02:00
# ifndef WINDOWS_ENABLED
FileAccess : : set_unix_permissions ( to_write , ( info . external_fa > > 16 ) & 0x01FF ) ;
# endif
2017-03-21 03:31:41 +01:00
ret = unzGoToNextFile ( pkg ) ;
fc + + ;
}
2017-12-17 19:47:50 +01:00
if ( p ) {
memdelete ( p ) ;
}
2017-03-21 03:31:41 +01:00
unzClose ( pkg ) ;
2021-05-20 22:15:49 +02:00
_update_template_status ( ) ;
2024-09-05 19:05:18 +02:00
EditorSettings : : get_singleton ( ) - > set ( " _export_template_download_directory " , p_file . get_base_dir ( ) ) ;
2018-08-13 12:46:02 +02:00
return true ;
2017-03-21 03:31:41 +01:00
}
2021-05-20 22:15:49 +02:00
void ExportTemplateManager : : _uninstall_template ( const String & p_version ) {
uninstall_confirm - > set_text ( vformat ( TTR ( " Remove templates for the version '%s'? " ) , p_version ) ) ;
uninstall_confirm - > popup_centered ( ) ;
uninstall_version = p_version ;
2017-03-21 03:31:41 +01:00
}
2021-05-20 22:15:49 +02:00
void ExportTemplateManager : : _uninstall_template_confirmed ( ) {
2022-03-23 10:08:58 +01:00
Ref < DirAccess > da = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
2022-07-29 02:36:26 +02:00
const String & templates_dir = EditorPaths : : get_singleton ( ) - > get_export_templates_dir ( ) ;
2017-11-04 19:08:13 +01:00
2021-05-20 22:15:49 +02:00
Error err = da - > change_dir ( templates_dir ) ;
ERR_FAIL_COND_MSG ( err ! = OK , " Could not access templates directory at ' " + templates_dir + " '. " ) ;
err = da - > change_dir ( uninstall_version ) ;
2022-08-30 02:34:01 +02:00
ERR_FAIL_COND_MSG ( err ! = OK , " Could not access templates directory at ' " + templates_dir . path_join ( uninstall_version ) + " '. " ) ;
2017-11-02 03:12:28 +01:00
2021-05-20 22:15:49 +02:00
err = da - > erase_contents_recursive ( ) ;
2022-08-30 02:34:01 +02:00
ERR_FAIL_COND_MSG ( err ! = OK , " Could not remove all templates in ' " + templates_dir . path_join ( uninstall_version ) + " '. " ) ;
2017-11-02 03:12:28 +01:00
2021-05-20 22:15:49 +02:00
da - > change_dir ( " .. " ) ;
err = da - > remove ( uninstall_version ) ;
2022-08-30 02:34:01 +02:00
ERR_FAIL_COND_MSG ( err ! = OK , " Could not remove templates directory at ' " + templates_dir . path_join ( uninstall_version ) + " '. " ) ;
2017-11-02 03:12:28 +01:00
2021-05-20 22:15:49 +02:00
_update_template_status ( ) ;
}
2017-11-02 03:12:28 +01:00
2021-05-20 22:15:49 +02:00
String ExportTemplateManager : : _get_selected_mirror ( ) const {
if ( mirrors_list - > get_item_count ( ) = = 1 ) {
return " " ;
2017-11-02 03:12:28 +01:00
}
2021-05-20 22:15:49 +02:00
int selected = mirrors_list - > get_selected_id ( ) ;
if ( selected = = 0 ) {
// This is a special "best available" value; so pick the first available mirror from the rest of the list.
selected = 1 ;
2017-11-02 03:12:28 +01:00
}
2021-05-20 22:15:49 +02:00
return mirrors_list - > get_item_metadata ( selected ) ;
2017-11-02 03:12:28 +01:00
}
2020-05-14 14:29:06 +02:00
2021-05-20 22:15:49 +02:00
void ExportTemplateManager : : _mirror_options_button_cbk ( int p_id ) {
switch ( p_id ) {
case VISIT_WEB_MIRROR : {
String mirror_url = _get_selected_mirror ( ) ;
if ( mirror_url . is_empty ( ) ) {
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " There are no mirrors available. " ) ) ;
return ;
}
OS : : get_singleton ( ) - > shell_open ( mirror_url ) ;
2017-11-02 03:12:28 +01:00
} break ;
2021-05-20 22:15:49 +02:00
case COPY_MIRROR_URL : {
String mirror_url = _get_selected_mirror ( ) ;
if ( mirror_url . is_empty ( ) ) {
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " There are no mirrors available. " ) ) ;
return ;
2017-11-02 03:12:28 +01:00
}
2021-05-20 22:15:49 +02:00
DisplayServer : : get_singleton ( ) - > clipboard_set ( mirror_url ) ;
2017-11-02 03:12:28 +01:00
} break ;
}
}
2021-09-18 09:33:18 +02:00
void ExportTemplateManager : : _installed_table_button_cbk ( Object * p_item , int p_column , int p_id , MouseButton p_button ) {
if ( p_button ! = MouseButton : : LEFT ) {
return ;
}
2021-05-20 22:15:49 +02:00
TreeItem * ti = Object : : cast_to < TreeItem > ( p_item ) ;
if ( ! ti ) {
2018-01-26 20:38:08 +01:00
return ;
}
2021-05-20 22:15:49 +02:00
switch ( p_id ) {
case OPEN_TEMPLATE_FOLDER : {
String version_string = ti - > get_text ( 0 ) ;
_open_template_folder ( version_string ) ;
} break ;
2017-11-02 03:12:28 +01:00
2021-05-20 22:15:49 +02:00
case UNINSTALL_TEMPLATE : {
String version_string = ti - > get_text ( 0 ) ;
_uninstall_template ( version_string ) ;
} break ;
2017-11-02 03:12:28 +01:00
}
2021-05-20 22:15:49 +02:00
}
2017-11-02 03:12:28 +01:00
2021-05-20 22:15:49 +02:00
void ExportTemplateManager : : _open_template_folder ( const String & p_version ) {
2022-07-29 02:36:26 +02:00
const String & templates_dir = EditorPaths : : get_singleton ( ) - > get_export_templates_dir ( ) ;
2022-12-07 04:33:35 +01:00
OS : : get_singleton ( ) - > shell_show_in_file_manager ( templates_dir . path_join ( p_version ) , true ) ;
2017-11-02 03:12:28 +01:00
}
2021-05-20 22:15:49 +02:00
void ExportTemplateManager : : popup_manager ( ) {
_update_template_status ( ) ;
2024-08-30 16:14:31 +02:00
switch ( _get_downloads_availability ( ) ) {
case DOWNLOADS_AVAILABLE : {
current_missing_label - > set_text ( TTR ( " Export templates are missing. Download them or install from a file. " ) ) ;
mirrors_list - > clear ( ) ;
mirrors_list - > add_item ( TTR ( " Best available mirror " ) , 0 ) ;
mirrors_list - > set_disabled ( false ) ;
mirrors_list - > set_tooltip_text ( " " ) ;
mirror_options_button - > set_disabled ( false ) ;
download_current_button - > set_disabled ( false ) ;
download_current_button - > set_tooltip_text ( " " ) ;
if ( ! is_downloading_templates ) {
_refresh_mirrors ( ) ;
}
} break ;
case DOWNLOADS_NOT_AVAILABLE_IN_OFFLINE_MODE : {
current_missing_label - > set_text ( TTR ( " Export templates are missing. Install them from a file. " ) ) ;
mirrors_list - > clear ( ) ;
mirrors_list - > add_item ( TTR ( " Not available in offline mode " ) , 0 ) ;
mirrors_list - > set_disabled ( true ) ;
mirrors_list - > set_tooltip_text ( TTR ( " Template downloading is disabled in offline mode. " ) ) ;
mirror_options_button - > set_disabled ( true ) ;
download_current_button - > set_disabled ( true ) ;
download_current_button - > set_tooltip_text ( TTR ( " Template downloading is disabled in offline mode. " ) ) ;
} break ;
case DOWNLOADS_NOT_AVAILABLE_FOR_DEV_BUILDS : {
current_missing_label - > set_text ( TTR ( " Export templates are missing. Install them from a file. " ) ) ;
mirrors_list - > clear ( ) ;
mirrors_list - > add_item ( TTR ( " No templates for development builds " ) , 0 ) ;
mirrors_list - > set_disabled ( true ) ;
mirrors_list - > set_tooltip_text ( TTR ( " Official export templates aren't available for development builds. " ) ) ;
mirror_options_button - > set_disabled ( true ) ;
download_current_button - > set_disabled ( true ) ;
download_current_button - > set_tooltip_text ( TTR ( " Official export templates aren't available for development builds. " ) ) ;
} break ;
2024-03-07 01:16:30 +01:00
}
2024-08-30 16:14:31 +02:00
2021-05-20 22:15:49 +02:00
popup_centered ( Size2 ( 720 , 280 ) * EDSCALE ) ;
2018-02-01 02:15:06 +01:00
}
2021-05-20 22:15:49 +02:00
void ExportTemplateManager : : ok_pressed ( ) {
if ( ! is_downloading_templates ) {
hide ( ) ;
return ;
2020-03-06 18:00:16 +01:00
}
2017-11-02 03:12:28 +01:00
2021-05-20 22:15:49 +02:00
hide_dialog_accept - > popup_centered ( ) ;
}
2017-11-02 03:12:28 +01:00
2021-05-20 22:15:49 +02:00
void ExportTemplateManager : : _hide_dialog ( ) {
hide ( ) ;
2017-11-02 03:12:28 +01:00
}
2024-02-13 21:20:23 +01:00
String ExportTemplateManager : : get_android_build_directory ( const Ref < EditorExportPreset > & p_preset ) {
if ( p_preset . is_valid ( ) ) {
String gradle_build_dir = p_preset - > get ( " gradle_build/gradle_build_directory " ) ;
if ( ! gradle_build_dir . is_empty ( ) ) {
return gradle_build_dir . path_join ( " build " ) ;
}
}
return " res://android/build " ;
}
String ExportTemplateManager : : get_android_source_zip ( const Ref < EditorExportPreset > & p_preset ) {
if ( p_preset . is_valid ( ) ) {
String android_source_zip = p_preset - > get ( " gradle_build/android_source_template " ) ;
if ( ! android_source_zip . is_empty ( ) ) {
return android_source_zip ;
}
}
2022-08-30 02:34:01 +02:00
const String templates_dir = EditorPaths : : get_singleton ( ) - > get_export_templates_dir ( ) . path_join ( VERSION_FULL_CONFIG ) ;
2024-02-13 21:20:23 +01:00
return templates_dir . path_join ( " android_source.zip " ) ;
2019-04-07 20:46:52 +02:00
}
2024-02-13 21:20:23 +01:00
String ExportTemplateManager : : get_android_template_identifier ( const Ref < EditorExportPreset > & p_preset ) {
// The template identifier is the Godot version for the default template, and the full path plus md5 hash for custom templates.
if ( p_preset . is_valid ( ) ) {
String android_source_zip = p_preset - > get ( " gradle_build/android_source_template " ) ;
if ( ! android_source_zip . is_empty ( ) ) {
return android_source_zip + String ( " [ " ) + FileAccess : : get_md5 ( android_source_zip ) + String ( " ] " ) ;
}
}
return VERSION_FULL_CONFIG ;
}
bool ExportTemplateManager : : is_android_template_installed ( const Ref < EditorExportPreset > & p_preset ) {
return DirAccess : : exists ( get_android_build_directory ( p_preset ) ) ;
}
bool ExportTemplateManager : : can_install_android_template ( const Ref < EditorExportPreset > & p_preset ) {
return FileAccess : : exists ( get_android_source_zip ( p_preset ) ) ;
}
Error ExportTemplateManager : : install_android_template ( const Ref < EditorExportPreset > & p_preset ) {
const String source_zip = get_android_source_zip ( p_preset ) ;
2021-07-15 15:12:56 +02:00
ERR_FAIL_COND_V ( ! FileAccess : : exists ( source_zip ) , ERR_CANT_OPEN ) ;
2024-02-13 21:20:23 +01:00
return install_android_template_from_file ( source_zip , p_preset ) ;
2021-07-15 15:12:56 +02:00
}
2024-02-13 21:20:23 +01:00
Error ExportTemplateManager : : install_android_template_from_file ( const String & p_file , const Ref < EditorExportPreset > & p_preset ) {
2019-09-03 02:31:51 +02:00
// To support custom Android builds, we install the Java source code and buildsystem
// from android_source.zip to the project's res://android folder.
2019-08-27 16:42:15 +02:00
2022-03-23 10:08:58 +01:00
Ref < DirAccess > da = DirAccess : : create ( DirAccess : : ACCESS_RESOURCES ) ;
ERR_FAIL_COND_V ( da . is_null ( ) , ERR_CANT_CREATE ) ;
2019-04-07 20:46:52 +02:00
2024-02-13 21:20:23 +01:00
String build_dir = get_android_build_directory ( p_preset ) ;
String parent_dir = build_dir . get_base_dir ( ) ;
// Make parent of the build dir (if it does not exist).
da - > make_dir_recursive ( parent_dir ) ;
2019-04-07 20:46:52 +02:00
{
2024-02-13 21:20:23 +01:00
// Add identifier, to ensure building won't work if the current template doesn't match.
Ref < FileAccess > f = FileAccess : : open ( parent_dir . path_join ( " .build_version " ) , FileAccess : : WRITE ) ;
2022-03-23 10:08:58 +01:00
ERR_FAIL_COND_V ( f . is_null ( ) , ERR_CANT_CREATE ) ;
2024-02-13 21:20:23 +01:00
f - > store_line ( get_android_template_identifier ( p_preset ) ) ;
2019-04-07 20:46:52 +02:00
}
2023-07-02 20:14:29 +02:00
// Create the android build directory.
2024-02-13 21:20:23 +01:00
Error err = da - > make_dir_recursive ( build_dir ) ;
2019-04-07 20:46:52 +02:00
ERR_FAIL_COND_V ( err ! = OK , err ) ;
2019-10-18 18:59:04 +02:00
{
// Add an empty .gdignore file to avoid scan.
2024-02-13 21:20:23 +01:00
Ref < FileAccess > f = FileAccess : : open ( build_dir . path_join ( " .gdignore " ) , FileAccess : : WRITE ) ;
2022-03-23 10:08:58 +01:00
ERR_FAIL_COND_V ( f . is_null ( ) , ERR_CANT_CREATE ) ;
2019-10-18 18:59:04 +02:00
f - > store_line ( " " ) ;
}
2019-04-07 20:46:52 +02:00
2019-08-27 16:42:15 +02:00
// Uncompress source template.
2022-05-11 14:15:58 +02:00
Ref < FileAccess > io_fa ;
zlib_filefunc_def io = zipio_create_io ( & io_fa ) ;
2019-04-07 20:46:52 +02:00
2021-07-15 15:12:56 +02:00
unzFile pkg = unzOpen2 ( p_file . utf8 ( ) . get_data ( ) , & io ) ;
2023-09-28 11:40:18 +02:00
ERR_FAIL_NULL_V_MSG ( pkg , ERR_CANT_OPEN , " Android sources not in ZIP format. " ) ;
2019-04-07 20:46:52 +02:00
int ret = unzGoToFirstFile ( pkg ) ;
int total_files = 0 ;
2019-08-27 16:42:15 +02:00
// Count files to unzip.
2019-04-07 20:46:52 +02:00
while ( ret = = UNZ_OK ) {
total_files + + ;
ret = unzGoToNextFile ( pkg ) ;
}
ret = unzGoToFirstFile ( pkg ) ;
2019-08-27 16:42:15 +02:00
ProgressDialog : : get_singleton ( ) - > add_task ( " uncompress_src " , TTR ( " Uncompressing Android Build Sources " ) , total_files ) ;
2019-04-07 20:46:52 +02:00
2022-05-19 17:00:06 +02:00
HashSet < String > dirs_tested ;
2019-04-07 20:46:52 +02:00
int idx = 0 ;
while ( ret = = UNZ_OK ) {
2019-08-27 16:42:15 +02:00
// Get file path.
2019-04-07 20:46:52 +02:00
unz_file_info info ;
2019-08-27 16:42:15 +02:00
char fpath [ 16384 ] ;
2020-04-02 01:20:12 +02:00
ret = unzGetCurrentFileInfo ( pkg , & info , fpath , 16384 , nullptr , 0 , nullptr , 0 ) ;
2022-04-05 12:40:26 +02:00
if ( ret ! = UNZ_OK ) {
break ;
}
2019-04-07 20:46:52 +02:00
2022-01-05 13:27:11 +01:00
String path = String : : utf8 ( fpath ) ;
2019-08-27 16:42:15 +02:00
String base_dir = path . get_base_dir ( ) ;
2019-04-07 20:46:52 +02:00
2019-08-27 16:42:15 +02:00
if ( ! path . ends_with ( " / " ) ) {
2022-09-29 11:53:28 +02:00
Vector < uint8_t > uncomp_data ;
uncomp_data . resize ( info . uncompressed_size ) ;
2019-04-07 20:46:52 +02:00
2019-08-27 16:42:15 +02:00
// Read.
2019-04-07 20:46:52 +02:00
unzOpenCurrentFile ( pkg ) ;
2022-09-29 11:53:28 +02:00
unzReadCurrentFile ( pkg , uncomp_data . ptrw ( ) , uncomp_data . size ( ) ) ;
2019-04-07 20:46:52 +02:00
unzCloseCurrentFile ( pkg ) ;
if ( ! dirs_tested . has ( base_dir ) ) {
2024-02-13 21:20:23 +01:00
da - > make_dir_recursive ( build_dir . path_join ( base_dir ) ) ;
2019-04-07 20:46:52 +02:00
dirs_tested . insert ( base_dir ) ;
}
2024-02-13 21:20:23 +01:00
String to_write = build_dir . path_join ( path ) ;
2022-03-23 10:08:58 +01:00
Ref < FileAccess > f = FileAccess : : open ( to_write , FileAccess : : WRITE ) ;
if ( f . is_valid ( ) ) {
2022-09-29 11:53:28 +02:00
f - > store_buffer ( uncomp_data . ptr ( ) , uncomp_data . size ( ) ) ;
2022-04-12 09:12:40 +02:00
f . unref ( ) ; // close file.
2019-04-07 20:46:52 +02:00
# ifndef WINDOWS_ENABLED
FileAccess : : set_unix_permissions ( to_write , ( info . external_fa > > 16 ) & 0x01FF ) ;
# endif
} else {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Can't uncompress file: " + to_write ) ;
2019-04-07 20:46:52 +02:00
}
}
2019-08-27 16:42:15 +02:00
ProgressDialog : : get_singleton ( ) - > task_step ( " uncompress_src " , path , idx ) ;
idx + + ;
ret = unzGoToNextFile ( pkg ) ;
}
ProgressDialog : : get_singleton ( ) - > end_task ( " uncompress_src " ) ;
unzClose ( pkg ) ;
2019-04-07 20:46:52 +02:00
return OK ;
}
2021-05-20 22:15:49 +02:00
void ExportTemplateManager : : _notification ( int p_what ) {
switch ( p_what ) {
2022-08-29 11:04:31 +02:00
case NOTIFICATION_ENTER_TREE :
2021-05-20 22:15:49 +02:00
case NOTIFICATION_THEME_CHANGED : {
2024-05-14 15:57:29 +02:00
current_value - > add_theme_font_override ( SceneStringName ( font ) , get_theme_font ( SNAME ( " main " ) , EditorStringName ( EditorFonts ) ) ) ;
current_missing_label - > add_theme_color_override ( SceneStringName ( font_color ) , get_theme_color ( SNAME ( " error_color " ) , EditorStringName ( Editor ) ) ) ;
current_installed_label - > add_theme_color_override ( SceneStringName ( font_color ) , get_theme_color ( SNAME ( " font_disabled_color " ) , EditorStringName ( Editor ) ) ) ;
2021-05-20 22:15:49 +02:00
2023-08-13 02:33:39 +02:00
mirror_options_button - > set_icon ( get_editor_theme_icon ( SNAME ( " GuiTabMenuHl " ) ) ) ;
2021-05-20 22:15:49 +02:00
} break ;
2017-03-21 03:31:41 +01:00
2021-05-20 22:15:49 +02:00
case NOTIFICATION_VISIBILITY_CHANGED : {
if ( ! is_visible ( ) ) {
set_process ( false ) ;
} else if ( is_visible ( ) & & is_downloading_templates ) {
set_process ( true ) ;
}
} break ;
2017-03-21 03:31:41 +01:00
2021-05-20 22:15:49 +02:00
case NOTIFICATION_PROCESS : {
update_countdown - = get_process_delta_time ( ) ;
if ( update_countdown > 0 ) {
return ;
}
update_countdown = 0.5 ;
2017-03-21 03:31:41 +01:00
2021-05-20 22:15:49 +02:00
String status ;
int downloaded_bytes ;
int total_bytes ;
bool success = _humanize_http_status ( download_templates , & status , & downloaded_bytes , & total_bytes ) ;
2017-03-21 03:31:41 +01:00
2021-05-20 22:15:49 +02:00
if ( downloaded_bytes > = 0 ) {
if ( total_bytes > 0 ) {
_set_current_progress_value ( float ( downloaded_bytes ) / total_bytes , status ) ;
} else {
_set_current_progress_value ( 0 , status ) ;
}
} else {
_set_current_progress_status ( status ) ;
}
2017-03-21 03:31:41 +01:00
2021-05-20 22:15:49 +02:00
if ( ! success ) {
set_process ( false ) ;
}
} break ;
2017-03-21 03:31:41 +01:00
2021-05-20 22:15:49 +02:00
case NOTIFICATION_WM_CLOSE_REQUEST : {
// This won't stop the window from closing, but will show the alert if the download is active.
ok_pressed ( ) ;
} break ;
}
}
2017-03-21 03:31:41 +01:00
2021-05-20 22:15:49 +02:00
ExportTemplateManager : : ExportTemplateManager ( ) {
2017-03-21 03:31:41 +01:00
set_title ( TTR ( " Export Template Manager " ) ) ;
set_hide_on_ok ( false ) ;
2022-07-08 02:31:19 +02:00
set_ok_button_text ( TTR ( " Close " ) ) ;
2021-05-20 22:15:49 +02:00
VBoxContainer * main_vb = memnew ( VBoxContainer ) ;
add_child ( main_vb ) ;
// Current version controls.
HBoxContainer * current_hb = memnew ( HBoxContainer ) ;
main_vb - > add_child ( current_hb ) ;
Label * current_label = memnew ( Label ) ;
2021-07-08 15:29:15 +02:00
current_label - > set_theme_type_variation ( " HeaderSmall " ) ;
2021-05-20 22:15:49 +02:00
current_label - > set_text ( TTR ( " Current Version: " ) ) ;
current_hb - > add_child ( current_label ) ;
current_value = memnew ( Label ) ;
current_hb - > add_child ( current_value ) ;
// Current version statuses.
// Status: Current version is missing.
current_missing_label = memnew ( Label ) ;
2021-07-08 15:29:15 +02:00
current_missing_label - > set_theme_type_variation ( " HeaderSmall " ) ;
2021-05-20 22:15:49 +02:00
current_missing_label - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2021-11-25 03:58:47 +01:00
current_missing_label - > set_horizontal_alignment ( HORIZONTAL_ALIGNMENT_RIGHT ) ;
2021-05-20 22:15:49 +02:00
current_hb - > add_child ( current_missing_label ) ;
// Status: Current version is installed.
current_installed_label = memnew ( Label ) ;
2021-07-08 15:29:15 +02:00
current_installed_label - > set_theme_type_variation ( " HeaderSmall " ) ;
2021-05-20 22:15:49 +02:00
current_installed_label - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2021-11-25 03:58:47 +01:00
current_installed_label - > set_horizontal_alignment ( HORIZONTAL_ALIGNMENT_RIGHT ) ;
2021-05-20 22:15:49 +02:00
current_installed_label - > set_text ( TTR ( " Export templates are installed and ready to be used. " ) ) ;
current_hb - > add_child ( current_installed_label ) ;
current_installed_label - > hide ( ) ;
// Currently installed template.
current_installed_hb = memnew ( HBoxContainer ) ;
main_vb - > add_child ( current_installed_hb ) ;
current_installed_path = memnew ( LineEdit ) ;
current_installed_path - > set_editable ( false ) ;
current_installed_path - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
current_installed_hb - > add_child ( current_installed_path ) ;
2024-06-16 21:14:34 +02:00
# ifndef ANDROID_ENABLED
Button * current_open_button = memnew ( Button ) ;
2021-05-20 22:15:49 +02:00
current_open_button - > set_text ( TTR ( " Open Folder " ) ) ;
2022-08-25 12:42:17 +02:00
current_open_button - > set_tooltip_text ( TTR ( " Open the folder containing installed templates for the current version. " ) ) ;
2021-05-20 22:15:49 +02:00
current_installed_hb - > add_child ( current_open_button ) ;
2024-05-14 09:40:21 +02:00
current_open_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & ExportTemplateManager : : _open_template_folder ) . bind ( VERSION_FULL_CONFIG ) ) ;
2024-06-16 21:14:34 +02:00
# endif
2021-05-20 22:15:49 +02:00
current_uninstall_button = memnew ( Button ) ;
current_uninstall_button - > set_text ( TTR ( " Uninstall " ) ) ;
2022-08-25 12:42:17 +02:00
current_uninstall_button - > set_tooltip_text ( TTR ( " Uninstall templates for the current version. " ) ) ;
2021-05-20 22:15:49 +02:00
current_installed_hb - > add_child ( current_uninstall_button ) ;
2024-05-14 09:40:21 +02:00
current_uninstall_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & ExportTemplateManager : : _uninstall_template ) . bind ( VERSION_FULL_CONFIG ) ) ;
2021-05-20 22:15:49 +02:00
main_vb - > add_child ( memnew ( HSeparator ) ) ;
// Download and install section.
HBoxContainer * install_templates_hb = memnew ( HBoxContainer ) ;
main_vb - > add_child ( install_templates_hb ) ;
// Download and install buttons are available.
install_options_vb = memnew ( VBoxContainer ) ;
install_options_vb - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
install_templates_hb - > add_child ( install_options_vb ) ;
HBoxContainer * download_install_hb = memnew ( HBoxContainer ) ;
install_options_vb - > add_child ( download_install_hb ) ;
Label * mirrors_label = memnew ( Label ) ;
mirrors_label - > set_text ( TTR ( " Download from: " ) ) ;
download_install_hb - > add_child ( mirrors_label ) ;
mirrors_list = memnew ( OptionButton ) ;
mirrors_list - > set_custom_minimum_size ( Size2 ( 280 , 0 ) * EDSCALE ) ;
download_install_hb - > add_child ( mirrors_list ) ;
request_mirrors = memnew ( HTTPRequest ) ;
mirrors_list - > add_child ( request_mirrors ) ;
request_mirrors - > connect ( " request_completed " , callable_mp ( this , & ExportTemplateManager : : _refresh_mirrors_completed ) ) ;
mirror_options_button = memnew ( MenuButton ) ;
2021-08-03 17:53:36 +02:00
mirror_options_button - > get_popup ( ) - > add_item ( TTR ( " Open in Web Browser " ) , VISIT_WEB_MIRROR ) ;
mirror_options_button - > get_popup ( ) - > add_item ( TTR ( " Copy Mirror URL " ) , COPY_MIRROR_URL ) ;
2021-05-20 22:15:49 +02:00
download_install_hb - > add_child ( mirror_options_button ) ;
2024-05-14 14:13:31 +02:00
mirror_options_button - > get_popup ( ) - > connect ( SceneStringName ( id_pressed ) , callable_mp ( this , & ExportTemplateManager : : _mirror_options_button_cbk ) ) ;
2021-05-20 22:15:49 +02:00
download_install_hb - > add_spacer ( ) ;
2024-08-30 16:14:31 +02:00
download_current_button = memnew ( Button ) ;
2021-05-20 22:15:49 +02:00
download_current_button - > set_text ( TTR ( " Download and Install " ) ) ;
2022-08-25 12:42:17 +02:00
download_current_button - > set_tooltip_text ( TTR ( " Download and install templates for the current version from the best possible mirror. " ) ) ;
2021-05-20 22:15:49 +02:00
download_install_hb - > add_child ( download_current_button ) ;
2024-05-14 09:40:21 +02:00
download_current_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & ExportTemplateManager : : _download_current ) ) ;
2021-05-20 22:15:49 +02:00
HBoxContainer * install_file_hb = memnew ( HBoxContainer ) ;
2021-11-25 03:58:47 +01:00
install_file_hb - > set_alignment ( BoxContainer : : ALIGNMENT_END ) ;
2021-05-20 22:15:49 +02:00
install_options_vb - > add_child ( install_file_hb ) ;
install_file_button = memnew ( Button ) ;
install_file_button - > set_text ( TTR ( " Install from File " ) ) ;
2022-08-25 12:42:17 +02:00
install_file_button - > set_tooltip_text ( TTR ( " Install templates from a local file. " ) ) ;
2021-05-20 22:15:49 +02:00
install_file_hb - > add_child ( install_file_button ) ;
2024-05-14 09:40:21 +02:00
install_file_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & ExportTemplateManager : : _install_file ) ) ;
2021-05-20 22:15:49 +02:00
// Templates are being downloaded; buttons unavailable.
download_progress_hb = memnew ( HBoxContainer ) ;
download_progress_hb - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
install_templates_hb - > add_child ( download_progress_hb ) ;
download_progress_hb - > hide ( ) ;
download_progress_bar = memnew ( ProgressBar ) ;
download_progress_bar - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
download_progress_bar - > set_v_size_flags ( Control : : SIZE_SHRINK_CENTER ) ;
download_progress_bar - > set_min ( 0 ) ;
download_progress_bar - > set_max ( 1 ) ;
download_progress_bar - > set_value ( 0 ) ;
download_progress_bar - > set_step ( 0.01 ) ;
2024-02-08 01:16:52 +01:00
download_progress_bar - > set_editor_preview_indeterminate ( true ) ;
2021-05-20 22:15:49 +02:00
download_progress_hb - > add_child ( download_progress_bar ) ;
download_progress_label = memnew ( Label ) ;
download_progress_label - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
download_progress_hb - > add_child ( download_progress_label ) ;
Button * download_cancel_button = memnew ( Button ) ;
download_cancel_button - > set_text ( TTR ( " Cancel " ) ) ;
2022-08-25 12:42:17 +02:00
download_cancel_button - > set_tooltip_text ( TTR ( " Cancel the download of the templates. " ) ) ;
2021-05-20 22:15:49 +02:00
download_progress_hb - > add_child ( download_cancel_button ) ;
2024-05-14 09:40:21 +02:00
download_cancel_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & ExportTemplateManager : : _cancel_template_download ) ) ;
2017-11-02 03:12:28 +01:00
download_templates = memnew ( HTTPRequest ) ;
2021-05-20 22:15:49 +02:00
install_templates_hb - > add_child ( download_templates ) ;
download_templates - > connect ( " request_completed " , callable_mp ( this , & ExportTemplateManager : : _download_template_completed ) ) ;
main_vb - > add_child ( memnew ( HSeparator ) ) ;
// Other installed templates table.
HBoxContainer * installed_versions_hb = memnew ( HBoxContainer ) ;
main_vb - > add_child ( installed_versions_hb ) ;
Label * installed_label = memnew ( Label ) ;
2021-07-08 15:29:15 +02:00
installed_label - > set_theme_type_variation ( " HeaderSmall " ) ;
2021-05-20 22:15:49 +02:00
installed_label - > set_text ( TTR ( " Other Installed Versions: " ) ) ;
installed_versions_hb - > add_child ( installed_label ) ;
installed_table = memnew ( Tree ) ;
2024-03-17 09:28:18 +01:00
installed_table - > set_auto_translate_mode ( AUTO_TRANSLATE_MODE_DISABLED ) ;
2021-05-20 22:15:49 +02:00
installed_table - > set_hide_root ( true ) ;
installed_table - > set_custom_minimum_size ( Size2 ( 0 , 100 ) * EDSCALE ) ;
installed_table - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
main_vb - > add_child ( installed_table ) ;
2021-09-18 09:33:18 +02:00
installed_table - > connect ( " button_clicked " , callable_mp ( this , & ExportTemplateManager : : _installed_table_button_cbk ) ) ;
2021-05-20 22:15:49 +02:00
// Dialogs.
uninstall_confirm = memnew ( ConfirmationDialog ) ;
uninstall_confirm - > set_title ( TTR ( " Uninstall Template " ) ) ;
add_child ( uninstall_confirm ) ;
2024-05-14 14:28:18 +02:00
uninstall_confirm - > connect ( SceneStringName ( confirmed ) , callable_mp ( this , & ExportTemplateManager : : _uninstall_template_confirmed ) ) ;
2021-05-20 22:15:49 +02:00
install_file_dialog = memnew ( FileDialog ) ;
install_file_dialog - > set_title ( TTR ( " Select Template File " ) ) ;
install_file_dialog - > set_access ( FileDialog : : ACCESS_FILESYSTEM ) ;
install_file_dialog - > set_file_mode ( FileDialog : : FILE_MODE_OPEN_FILE ) ;
2024-09-05 19:05:18 +02:00
install_file_dialog - > set_current_dir ( EDITOR_DEF ( " _export_template_download_directory " , " " ) ) ;
2022-07-04 23:26:26 +02:00
install_file_dialog - > add_filter ( " *.tpz " , TTR ( " Godot Export Templates " ) ) ;
2022-07-28 22:56:41 +02:00
install_file_dialog - > connect ( " file_selected " , callable_mp ( this , & ExportTemplateManager : : _install_file_selected ) . bind ( false ) ) ;
2021-05-20 22:15:49 +02:00
add_child ( install_file_dialog ) ;
hide_dialog_accept = memnew ( AcceptDialog ) ;
hide_dialog_accept - > set_text ( TTR ( " The templates will continue to download. \n You may experience a short editor freeze when they finish. " ) ) ;
add_child ( hide_dialog_accept ) ;
2024-05-14 14:28:18 +02:00
hide_dialog_accept - > connect ( SceneStringName ( confirmed ) , callable_mp ( this , & ExportTemplateManager : : _hide_dialog ) ) ;
2017-03-21 03:31:41 +01:00
}