2017-04-28 18:04:09 +00:00
/*************************************************************************/
/* export_template_manager.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 12:16:55 +00:00
/* https://godotengine.org */
2017-04-28 18:04:09 +00:00
/*************************************************************************/
2019-01-01 11:53:14 +00:00
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
2017-04-28 18:04:09 +00:00
/* */
/* 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-04 23:50:27 +00:00
2017-03-21 02:31:41 +00:00
# include "export_template_manager.h"
2017-04-28 18:04:09 +00:00
2018-09-11 16:13:45 +00:00
# include "core/io/json.h"
# include "core/io/zip_io.h"
# include "core/os/dir_access.h"
2018-01-26 19:38:08 +00:00
# include "core/os/input.h"
# include "core/os/keyboard.h"
2018-09-11 16:13:45 +00:00
# include "core/version.h"
2017-03-21 02:31:41 +00:00
# include "editor_node.h"
# include "editor_scale.h"
2017-11-17 20:48:24 +00:00
2017-03-21 02:31:41 +00:00
void ExportTemplateManager : : _update_template_list ( ) {
while ( current_hb - > get_child_count ( ) ) {
memdelete ( current_hb - > get_child ( 0 ) ) ;
}
while ( installed_vb - > get_child_count ( ) ) {
memdelete ( installed_vb - > get_child ( 0 ) ) ;
}
DirAccess * d = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
2017-11-17 20:48:24 +00:00
Error err = d - > change_dir ( EditorSettings : : get_singleton ( ) - > get_templates_dir ( ) ) ;
2017-03-21 02:31:41 +00:00
Set < String > templates ;
2019-07-25 09:09:57 +00:00
d - > list_dir_begin ( ) ;
2017-03-21 02:31:41 +00:00
if ( err = = OK ) {
2019-07-25 09:09:57 +00:00
String c = d - > get_next ( ) ;
2017-03-21 02:31:41 +00:00
while ( c ! = String ( ) ) {
2019-07-25 09:09:57 +00:00
if ( d - > current_is_dir ( ) & & ! c . begins_with ( " . " ) ) {
2017-03-21 02:31:41 +00:00
templates . insert ( c ) ;
}
2019-07-25 09:09:57 +00:00
c = d - > get_next ( ) ;
2017-03-21 02:31:41 +00:00
}
}
d - > list_dir_end ( ) ;
memdelete ( d ) ;
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 18:48:49 +00:00
String current_version = VERSION_FULL_CONFIG ;
2019-08-12 15:05:25 +00:00
// Downloadable export templates are only available for stable, alpha, beta and RC versions.
// Therefore, don't display download-related features when using a development version
const bool downloads_available = String ( VERSION_STATUS ) ! = String ( " dev " ) ;
2017-03-21 02:31:41 +00:00
Label * current = memnew ( Label ) ;
current - > set_h_size_flags ( SIZE_EXPAND_FILL ) ;
current_hb - > add_child ( current ) ;
if ( templates . has ( current_version ) ) {
2017-09-26 02:43:20 +00:00
current - > add_color_override ( " font_color " , get_color ( " success_color " , " Editor " ) ) ;
2019-08-12 15:05:25 +00:00
// Only display a redownload button if it can be downloaded in the first place
if ( downloads_available ) {
Button * redownload = memnew ( Button ) ;
redownload - > set_text ( TTR ( " Redownload " ) ) ;
current_hb - > add_child ( redownload ) ;
redownload - > connect ( " pressed " , this , " _download_template " , varray ( current_version ) ) ;
}
2017-03-21 02:31:41 +00:00
Button * uninstall = memnew ( Button ) ;
uninstall - > set_text ( TTR ( " Uninstall " ) ) ;
current_hb - > add_child ( uninstall ) ;
current - > set_text ( current_version + " " + TTR ( " (Installed) " ) ) ;
uninstall - > connect ( " pressed " , this , " _uninstall_template " , varray ( current_version ) ) ;
} else {
2017-09-26 02:43:20 +00:00
current - > add_color_override ( " font_color " , get_color ( " error_color " , " Editor " ) ) ;
2017-03-21 02:31:41 +00:00
Button * redownload = memnew ( Button ) ;
redownload - > set_text ( TTR ( " Download " ) ) ;
2019-08-12 15:05:25 +00:00
if ( ! downloads_available ) {
redownload - > set_disabled ( true ) ;
redownload - > set_tooltip ( TTR ( " Official export templates aren't available for development builds. " ) ) ;
}
2017-03-21 02:31:41 +00:00
redownload - > connect ( " pressed " , this , " _download_template " , varray ( current_version ) ) ;
current_hb - > add_child ( redownload ) ;
current - > set_text ( current_version + " " + TTR ( " (Missing) " ) ) ;
}
for ( Set < String > : : Element * E = templates . back ( ) ; E ; E = E - > prev ( ) ) {
HBoxContainer * hbc = memnew ( HBoxContainer ) ;
Label * version = memnew ( Label ) ;
2017-09-26 02:43:20 +00:00
version - > set_modulate ( get_color ( " disabled_font_color " , " Editor " ) ) ;
2017-03-21 02:31:41 +00:00
String text = E - > get ( ) ;
if ( text = = current_version ) {
text + = " " + TTR ( " (Current) " ) ;
}
version - > set_text ( text ) ;
version - > set_h_size_flags ( SIZE_EXPAND_FILL ) ;
hbc - > add_child ( version ) ;
Button * uninstall = memnew ( Button ) ;
uninstall - > set_text ( TTR ( " Uninstall " ) ) ;
hbc - > add_child ( uninstall ) ;
uninstall - > connect ( " pressed " , this , " _uninstall_template " , varray ( E - > get ( ) ) ) ;
installed_vb - > add_child ( hbc ) ;
}
}
void ExportTemplateManager : : _download_template ( const String & p_version ) {
2017-11-02 02:12:28 +00:00
while ( template_list - > get_child_count ( ) ) {
memdelete ( template_list - > get_child ( 0 ) ) ;
}
template_downloader - > popup_centered_minsize ( ) ;
2018-04-22 17:36:01 +00:00
template_list_state - > set_text ( TTR ( " Retrieving mirrors, please wait... " ) ) ;
2017-11-02 02:12:28 +00:00
template_download_progress - > set_max ( 100 ) ;
template_download_progress - > set_value ( 0 ) ;
2017-11-04 18:08:13 +00:00
request_mirror - > request ( " https://godotengine.org/mirrorlist/ " + p_version + " .json " ) ;
2017-11-02 02:12:28 +00:00
template_list_state - > show ( ) ;
template_download_progress - > show ( ) ;
2017-03-21 02:31:41 +00:00
}
void ExportTemplateManager : : _uninstall_template ( const String & p_version ) {
remove_confirm - > set_text ( vformat ( TTR ( " Remove template version '%s'? " ) , p_version ) ) ;
remove_confirm - > popup_centered_minsize ( ) ;
to_remove = p_version ;
}
void ExportTemplateManager : : _uninstall_template_confirm ( ) {
DirAccess * d = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
2017-11-17 20:48:24 +00:00
Error err = d - > change_dir ( EditorSettings : : get_singleton ( ) - > get_templates_dir ( ) ) ;
2017-03-21 02:31:41 +00:00
ERR_FAIL_COND ( err ! = OK ) ;
err = d - > change_dir ( to_remove ) ;
ERR_FAIL_COND ( err ! = OK ) ;
Vector < String > files ;
d - > list_dir_begin ( ) ;
2019-07-25 09:09:57 +00:00
String c = d - > get_next ( ) ;
2017-03-21 02:31:41 +00:00
while ( c ! = String ( ) ) {
2019-07-25 09:09:57 +00:00
if ( ! d - > current_is_dir ( ) ) {
2017-03-21 02:31:41 +00:00
files . push_back ( c ) ;
}
2019-07-25 09:09:57 +00:00
c = d - > get_next ( ) ;
2017-03-21 02:31:41 +00:00
}
d - > list_dir_end ( ) ;
for ( int i = 0 ; i < files . size ( ) ; i + + ) {
d - > remove ( files [ i ] ) ;
}
d - > change_dir ( " .. " ) ;
d - > remove ( to_remove ) ;
_update_template_list ( ) ;
}
2018-08-13 10:46:02 +00:00
bool ExportTemplateManager : : _install_from_file ( const String & p_file , bool p_use_progress ) {
2017-03-21 02:31:41 +00:00
FileAccess * fa = NULL ;
zlib_filefunc_def io = zipio_create_io_from_file ( & fa ) ;
unzFile pkg = unzOpen2 ( p_file . utf8 ( ) . get_data ( ) , & io ) ;
if ( ! pkg ) {
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " Can't open export templates zip. " ) ) ;
2018-08-13 10:46:02 +00:00
return false ;
2017-03-21 02:31:41 +00:00
}
int ret = unzGoToFirstFile ( pkg ) ;
int fc = 0 ; //count them and find version
String version ;
2019-02-25 18:37:51 +00:00
String contents_dir ;
2017-03-21 02:31:41 +00:00
while ( ret = = UNZ_OK ) {
unz_file_info info ;
char fname [ 16384 ] ;
ret = unzGetCurrentFileInfo ( pkg , & info , fname , 16384 , NULL , 0 , NULL , 0 ) ;
String file = fname ;
if ( file . ends_with ( " version.txt " ) ) {
Vector < uint8_t > data ;
data . resize ( info . uncompressed_size ) ;
//read
2017-08-21 19:15:36 +00:00
unzOpenCurrentFile ( pkg ) ;
2017-11-25 03:07:54 +00:00
ret = unzReadCurrentFile ( pkg , data . ptrw ( ) , data . size ( ) ) ;
2017-03-21 02:31:41 +00:00
unzCloseCurrentFile ( pkg ) ;
String data_str ;
data_str . parse_utf8 ( ( const char * ) data . ptr ( ) , data . size ( ) ) ;
data_str = data_str . strip_edges ( ) ;
2018-02-25 14:24:33 +00: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 ) {
EditorNode : : get_singleton ( ) - > show_warning ( vformat ( TTR ( " Invalid version.txt format inside templates: %s. " ) , data_str ) ) ;
2017-03-21 02:31:41 +00:00
unzClose ( pkg ) ;
2018-08-13 10:46:02 +00:00
return false ;
2017-03-21 02:31:41 +00:00
}
2018-02-25 14:24:33 +00:00
version = data_str ;
2019-02-25 18:37:51 +00:00
contents_dir = file . get_base_dir ( ) . trim_suffix ( " / " ) . trim_suffix ( " \\ " ) ;
2017-03-21 02:31:41 +00:00
}
2018-07-16 07:05:45 +00:00
if ( file . get_file ( ) . size ( ) ! = 0 ) {
fc + + ;
}
2017-03-21 02:31:41 +00:00
ret = unzGoToNextFile ( pkg ) ;
}
if ( version = = String ( ) ) {
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " No version.txt found inside templates. " ) ) ;
unzClose ( pkg ) ;
2018-08-13 10:46:02 +00:00
return false ;
2017-03-21 02:31:41 +00:00
}
2017-11-17 20:48:24 +00:00
String template_path = EditorSettings : : get_singleton ( ) - > get_templates_dir ( ) . plus_file ( version ) ;
2017-03-21 02:31:41 +00:00
DirAccess * d = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
Error err = d - > make_dir_recursive ( template_path ) ;
if ( err ! = OK ) {
2018-01-04 19:00:39 +00:00
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " Error creating path for templates: " ) + " \n " + template_path ) ;
2017-03-21 02:31:41 +00:00
unzClose ( pkg ) ;
2018-08-13 10:46:02 +00:00
return false ;
2017-03-21 02:31:41 +00:00
}
memdelete ( d ) ;
ret = unzGoToFirstFile ( pkg ) ;
2017-12-17 18:47:50 +00:00
EditorProgress * p = NULL ;
if ( p_use_progress ) {
p = memnew ( EditorProgress ( " ltask " , TTR ( " Extracting Export Templates " ) , fc ) ) ;
}
2017-03-21 02:31:41 +00:00
fc = 0 ;
while ( ret = = UNZ_OK ) {
//get filename
unz_file_info info ;
char fname [ 16384 ] ;
2017-08-21 19:15:36 +00:00
unzGetCurrentFileInfo ( pkg , & info , fname , 16384 , NULL , 0 , NULL , 0 ) ;
2017-03-21 02:31:41 +00:00
2019-02-27 23:25:55 +00:00
String file_path ( String ( fname ) . simplify_path ( ) ) ;
2019-02-25 18:37:51 +00:00
String file = file_path . get_file ( ) ;
2017-03-21 02:31:41 +00:00
2018-07-16 07:05:45 +00:00
if ( file . size ( ) = = 0 ) {
ret = unzGoToNextFile ( pkg ) ;
continue ;
}
2017-03-21 02:31:41 +00:00
Vector < uint8_t > data ;
data . resize ( info . uncompressed_size ) ;
//read
2017-08-21 19:15:36 +00:00
unzOpenCurrentFile ( pkg ) ;
2017-11-25 03:07:54 +00:00
unzReadCurrentFile ( pkg , data . ptrw ( ) , data . size ( ) ) ;
2017-03-21 02:31:41 +00:00
unzCloseCurrentFile ( pkg ) ;
2019-02-27 23:25:55 +00:00
String base_dir = file_path . get_base_dir ( ) . trim_suffix ( " / " ) ;
2019-02-25 18:37:51 +00:00
if ( base_dir ! = contents_dir & & base_dir . begins_with ( contents_dir ) ) {
2019-02-27 23:25:55 +00:00
base_dir = base_dir . substr ( contents_dir . length ( ) , file_path . length ( ) ) . trim_prefix ( " / " ) ;
2019-02-25 18:37:51 +00:00
file = base_dir . plus_file ( file ) ;
DirAccessRef da = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
ERR_CONTINUE ( ! da ) ;
String output_dir = template_path . plus_file ( base_dir ) ;
if ( ! DirAccess : : exists ( output_dir ) ) {
Error mkdir_err = da - > make_dir_recursive ( output_dir ) ;
ERR_CONTINUE ( mkdir_err ! = OK ) ;
}
}
2017-12-17 18:47:50 +00:00
if ( p ) {
p - > step ( TTR ( " Importing: " ) + " " + file , fc ) ;
}
2017-03-21 02:31:41 +00:00
2019-04-07 18:46:52 +00:00
String to_write = template_path . plus_file ( file ) ;
FileAccess * f = FileAccess : : open ( to_write , FileAccess : : WRITE ) ;
2017-03-21 02:31:41 +00:00
2017-12-18 19:44:19 +00:00
if ( ! f ) {
ret = unzGoToNextFile ( pkg ) ;
fc + + ;
2019-08-15 02:57:49 +00:00
ERR_CONTINUE_MSG ( true , " Can't open file from path: " + String ( to_write ) + " . " ) ;
2017-12-18 19:44:19 +00:00
}
2017-03-21 02:31:41 +00:00
f - > store_buffer ( data . ptr ( ) , data . size ( ) ) ;
memdelete ( f ) ;
2019-04-07 18:46:52 +00:00
# ifndef WINDOWS_ENABLED
FileAccess : : set_unix_permissions ( to_write , ( info . external_fa > > 16 ) & 0x01FF ) ;
# endif
2017-03-21 02:31:41 +00:00
ret = unzGoToNextFile ( pkg ) ;
fc + + ;
}
2017-12-17 18:47:50 +00:00
if ( p ) {
memdelete ( p ) ;
}
2017-03-21 02:31:41 +00:00
unzClose ( pkg ) ;
_update_template_list ( ) ;
2018-08-13 10:46:02 +00:00
return true ;
2017-03-21 02:31:41 +00:00
}
void ExportTemplateManager : : popup_manager ( ) {
_update_template_list ( ) ;
2017-09-26 02:43:20 +00:00
popup_centered_minsize ( Size2 ( 400 , 400 ) * EDSCALE ) ;
2017-03-21 02:31:41 +00:00
}
void ExportTemplateManager : : ok_pressed ( ) {
template_open - > popup_centered_ratio ( ) ;
}
2017-11-02 02:12:28 +00:00
void ExportTemplateManager : : _http_download_mirror_completed ( int p_status , int p_code , const PoolStringArray & headers , const PoolByteArray & p_data ) {
2017-11-04 18:08:13 +00:00
if ( p_status ! = HTTPRequest : : RESULT_SUCCESS | | p_code ! = 200 ) {
EditorNode : : get_singleton ( ) - > show_warning ( " Error getting the list of mirrors. " ) ;
return ;
}
String mirror_str ;
{
PoolByteArray : : Read r = p_data . read ( ) ;
mirror_str . parse_utf8 ( ( const char * ) r . ptr ( ) , p_data . size ( ) ) ;
}
2017-11-02 02:12:28 +00:00
template_list_state - > hide ( ) ;
template_download_progress - > hide ( ) ;
Variant r ;
String errs ;
int errline ;
Error err = JSON : : parse ( mirror_str , r , errs , errline ) ;
if ( err ! = OK ) {
2017-11-04 18:08:13 +00:00
EditorNode : : get_singleton ( ) - > show_warning ( " Error parsing JSON of mirror list. Please report this issue! " ) ;
2017-11-02 02:12:28 +00:00
return ;
}
bool mirrors_found = false ;
Dictionary d = r ;
if ( d . has ( " mirrors " ) ) {
Array mirrors = d [ " mirrors " ] ;
for ( int i = 0 ; i < mirrors . size ( ) ; i + + ) {
Dictionary m = mirrors [ i ] ;
ERR_CONTINUE ( ! m . has ( " url " ) | | ! m . has ( " name " ) ) ;
LinkButton * lb = memnew ( LinkButton ) ;
lb - > set_text ( m [ " name " ] ) ;
lb - > connect ( " pressed " , this , " _begin_template_download " , varray ( m [ " url " ] ) ) ;
template_list - > add_child ( lb ) ;
mirrors_found = true ;
}
}
if ( ! mirrors_found ) {
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " No download links found for this version. Direct download is only available for official releases. " ) ) ;
return ;
}
}
void ExportTemplateManager : : _http_download_templates_completed ( int p_status , int p_code , const PoolStringArray & headers , const PoolByteArray & p_data ) {
switch ( p_status ) {
case HTTPRequest : : RESULT_CANT_RESOLVE : {
template_list_state - > set_text ( TTR ( " Can't resolve. " ) ) ;
} break ;
case HTTPRequest : : RESULT_BODY_SIZE_LIMIT_EXCEEDED :
case HTTPRequest : : RESULT_CONNECTION_ERROR :
2019-06-26 13:08:25 +00:00
case HTTPRequest : : RESULT_CHUNKED_BODY_SIZE_MISMATCH :
2017-11-02 02:12:28 +00:00
case HTTPRequest : : RESULT_SSL_HANDSHAKE_ERROR :
case HTTPRequest : : RESULT_CANT_CONNECT : {
template_list_state - > set_text ( TTR ( " Can't connect. " ) ) ;
} break ;
case HTTPRequest : : RESULT_NO_RESPONSE : {
template_list_state - > set_text ( TTR ( " No response. " ) ) ;
} break ;
case HTTPRequest : : RESULT_REQUEST_FAILED : {
2017-12-12 15:57:17 +00:00
template_list_state - > set_text ( TTR ( " Request Failed. " ) ) ;
2017-11-02 02:12:28 +00:00
} break ;
case HTTPRequest : : RESULT_REDIRECT_LIMIT_REACHED : {
template_list_state - > set_text ( TTR ( " Redirect Loop. " ) ) ;
} break ;
default : {
if ( p_code ! = 200 ) {
template_list_state - > set_text ( TTR ( " Failed: " ) + " " + itos ( p_code ) ) ;
} else {
2018-02-07 15:18:14 +00:00
String path = download_templates - > get_download_file ( ) ;
template_list_state - > set_text ( TTR ( " Download Complete. " ) ) ;
template_downloader - > hide ( ) ;
2019-08-09 11:45:30 +00:00
bool ret = _install_from_file ( path , false ) ;
2018-08-13 10:46:02 +00:00
if ( ret ) {
2019-08-09 11:45:30 +00:00
// Clean up downloaded file.
DirAccessRef da = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
Error err = da - > remove ( path ) ;
2018-08-13 10:46:02 +00:00
if ( err ! = OK ) {
2019-08-09 11:45:30 +00:00
EditorNode : : get_singleton ( ) - > add_io_error ( TTR ( " Cannot remove temporary file: " ) + " \n " + path + " \n " ) ;
2018-08-13 10:46:02 +00:00
}
} else {
2019-08-09 11:45:30 +00:00
EditorNode : : get_singleton ( ) - > add_io_error ( vformat ( TTR ( " Templates installation failed. \n The problematic templates archives can be found at '%s'. " ) , path ) ) ;
2018-08-13 10:46:02 +00:00
}
2017-11-02 02:12:28 +00:00
}
} break ;
}
set_process ( false ) ;
}
void ExportTemplateManager : : _begin_template_download ( const String & p_url ) {
2018-01-26 19:38:08 +00:00
if ( Input : : get_singleton ( ) - > is_key_pressed ( KEY_SHIFT ) ) {
OS : : get_singleton ( ) - > shell_open ( p_url ) ;
return ;
}
2017-11-02 02:12:28 +00:00
for ( int i = 0 ; i < template_list - > get_child_count ( ) ; i + + ) {
BaseButton * b = Object : : cast_to < BaseButton > ( template_list - > get_child ( 0 ) ) ;
if ( b ) {
b - > set_disabled ( true ) ;
}
}
download_data . clear ( ) ;
2018-02-07 15:18:14 +00:00
download_templates - > set_download_file ( EditorSettings : : get_singleton ( ) - > get_cache_dir ( ) . plus_file ( " tmp_templates.tpz " ) ) ;
download_templates - > set_use_threads ( true ) ;
2017-11-02 02:12:28 +00:00
Error err = download_templates - > request ( p_url ) ;
if ( err ! = OK ) {
2019-08-09 11:45:30 +00:00
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " Error requesting URL: " ) + " " + p_url ) ;
2017-11-02 02:12:28 +00:00
return ;
}
set_process ( true ) ;
template_list_state - > show ( ) ;
template_download_progress - > set_max ( 100 ) ;
template_download_progress - > set_value ( 0 ) ;
template_download_progress - > show ( ) ;
2018-04-22 17:36:01 +00:00
template_list_state - > set_text ( TTR ( " Connecting to Mirror... " ) ) ;
2017-11-02 02:12:28 +00:00
}
2018-02-01 01:15:06 +00:00
void ExportTemplateManager : : _window_template_downloader_closed ( ) {
download_templates - > cancel_request ( ) ;
}
2017-11-02 02:12:28 +00:00
void ExportTemplateManager : : _notification ( int p_what ) {
if ( p_what = = NOTIFICATION_PROCESS ) {
update_countdown - = get_process_delta_time ( ) ;
if ( update_countdown > 0 ) {
return ;
}
update_countdown = 0.5 ;
String status ;
bool errored = false ;
switch ( download_templates - > get_http_client_status ( ) ) {
case HTTPClient : : STATUS_DISCONNECTED :
status = TTR ( " Disconnected " ) ;
errored = true ;
break ;
case HTTPClient : : STATUS_RESOLVING : status = TTR ( " Resolving " ) ; break ;
case HTTPClient : : STATUS_CANT_RESOLVE :
status = TTR ( " Can't Resolve " ) ;
errored = true ;
break ;
2018-04-22 17:36:01 +00:00
case HTTPClient : : STATUS_CONNECTING : status = TTR ( " Connecting... " ) ; break ;
2017-11-02 02:12:28 +00:00
case HTTPClient : : STATUS_CANT_CONNECT :
2017-12-12 15:57:17 +00:00
status = TTR ( " Can't Connect " ) ;
2017-11-02 02:12:28 +00:00
errored = true ;
break ;
case HTTPClient : : STATUS_CONNECTED : status = TTR ( " Connected " ) ; break ;
2018-04-22 17:36:01 +00:00
case HTTPClient : : STATUS_REQUESTING : status = TTR ( " Requesting... " ) ; break ;
2017-11-02 02:12:28 +00:00
case HTTPClient : : STATUS_BODY :
status = TTR ( " Downloading " ) ;
if ( download_templates - > get_body_size ( ) > 0 ) {
status + = " " + String : : humanize_size ( download_templates - > get_downloaded_bytes ( ) ) + " / " + String : : humanize_size ( download_templates - > get_body_size ( ) ) ;
template_download_progress - > set_max ( download_templates - > get_body_size ( ) ) ;
template_download_progress - > set_value ( download_templates - > get_downloaded_bytes ( ) ) ;
} else {
status + = " " + String : : humanize_size ( download_templates - > get_downloaded_bytes ( ) ) ;
}
break ;
case HTTPClient : : STATUS_CONNECTION_ERROR :
status = TTR ( " Connection Error " ) ;
errored = true ;
break ;
case HTTPClient : : STATUS_SSL_HANDSHAKE_ERROR :
status = TTR ( " SSL Handshake Error " ) ;
errored = true ;
break ;
}
template_list_state - > set_text ( status ) ;
if ( errored ) {
set_process ( false ) ;
}
}
if ( p_what = = NOTIFICATION_VISIBILITY_CHANGED ) {
if ( ! is_visible_in_tree ( ) ) {
set_process ( false ) ;
}
}
}
2019-04-07 18:46:52 +00:00
bool ExportTemplateManager : : can_install_android_template ( ) {
2019-08-27 14:42:15 +00:00
const String templates_dir = EditorSettings : : get_singleton ( ) - > get_templates_dir ( ) . plus_file ( VERSION_FULL_CONFIG ) ;
return FileAccess : : exists ( templates_dir . plus_file ( " android_source.zip " ) ) & &
FileAccess : : exists ( templates_dir . plus_file ( " android_release.apk " ) ) & &
FileAccess : : exists ( templates_dir . plus_file ( " android_debug.apk " ) ) ;
2019-04-07 18:46:52 +00:00
}
Error ExportTemplateManager : : install_android_template ( ) {
2019-08-27 14:42:15 +00:00
// To support custom Android builds, we install various things to the project's res://android folder.
// First is the Java source code and buildsystem from android_source.zip.
// Then we extract the Godot Android libraries from pre-build android_release.apk
// and android_debug.apk, to place them in the libs folder.
2019-04-07 18:46:52 +00:00
DirAccessRef da = DirAccess : : open ( " res:// " ) ;
ERR_FAIL_COND_V ( ! da , ERR_CANT_CREATE ) ;
2019-08-27 14:42:15 +00:00
// Make res://android dir (if it does not exist).
2019-04-07 18:46:52 +00:00
da - > make_dir ( " android " ) ;
{
2019-08-27 14:42:15 +00:00
// Add an empty .gdignore file to avoid scan.
2019-04-07 18:46:52 +00:00
FileAccessRef f = FileAccess : : open ( " res://android/.gdignore " , FileAccess : : WRITE ) ;
ERR_FAIL_COND_V ( ! f , ERR_CANT_CREATE ) ;
f - > store_line ( " " ) ;
f - > close ( ) ;
}
{
2019-08-27 14:42:15 +00:00
// Add version, to ensure building won't work if template and Godot version don't match.
2019-04-07 18:46:52 +00:00
FileAccessRef f = FileAccess : : open ( " res://android/.build_version " , FileAccess : : WRITE ) ;
ERR_FAIL_COND_V ( ! f , ERR_CANT_CREATE ) ;
f - > store_line ( VERSION_FULL_CONFIG ) ;
f - > close ( ) ;
}
Error err = da - > make_dir_recursive ( " android/build " ) ;
ERR_FAIL_COND_V ( err ! = OK , err ) ;
2019-08-27 14:42:15 +00:00
// Uncompress source template.
const String & templates_path = EditorSettings : : get_singleton ( ) - > get_templates_dir ( ) . plus_file ( VERSION_FULL_CONFIG ) ;
const String & source_zip = templates_path . plus_file ( " android_source.zip " ) ;
2019-04-07 18:46:52 +00:00
ERR_FAIL_COND_V ( ! FileAccess : : exists ( source_zip ) , ERR_CANT_OPEN ) ;
FileAccess * src_f = NULL ;
zlib_filefunc_def io = zipio_create_io_from_file ( & src_f ) ;
unzFile pkg = unzOpen2 ( source_zip . utf8 ( ) . get_data ( ) , & io ) ;
2019-08-21 17:32:58 +00:00
ERR_FAIL_COND_V_MSG ( ! pkg , ERR_CANT_OPEN , " Android sources not in ZIP format. " ) ;
2019-04-07 18:46:52 +00:00
int ret = unzGoToFirstFile ( pkg ) ;
int total_files = 0 ;
2019-08-27 14:42:15 +00:00
// Count files to unzip.
2019-04-07 18:46:52 +00:00
while ( ret = = UNZ_OK ) {
total_files + + ;
ret = unzGoToNextFile ( pkg ) ;
}
ret = unzGoToFirstFile ( pkg ) ;
2019-08-27 14:42:15 +00:00
ProgressDialog : : get_singleton ( ) - > add_task ( " uncompress_src " , TTR ( " Uncompressing Android Build Sources " ) , total_files ) ;
2019-04-07 18:46:52 +00:00
2019-08-27 14:42:15 +00:00
Set < String > dirs_tested ;
2019-04-07 18:46:52 +00:00
int idx = 0 ;
while ( ret = = UNZ_OK ) {
2019-08-27 14:42:15 +00:00
// Get file path.
2019-04-07 18:46:52 +00:00
unz_file_info info ;
2019-08-27 14:42:15 +00:00
char fpath [ 16384 ] ;
ret = unzGetCurrentFileInfo ( pkg , & info , fpath , 16384 , NULL , 0 , NULL , 0 ) ;
2019-04-07 18:46:52 +00:00
2019-08-27 14:42:15 +00:00
String path = fpath ;
String base_dir = path . get_base_dir ( ) ;
2019-04-07 18:46:52 +00:00
2019-08-27 14:42:15 +00:00
if ( ! path . ends_with ( " / " ) ) {
2019-04-07 18:46:52 +00:00
Vector < uint8_t > data ;
data . resize ( info . uncompressed_size ) ;
2019-08-27 14:42:15 +00:00
// Read.
2019-04-07 18:46:52 +00:00
unzOpenCurrentFile ( pkg ) ;
unzReadCurrentFile ( pkg , data . ptrw ( ) , data . size ( ) ) ;
unzCloseCurrentFile ( pkg ) ;
if ( ! dirs_tested . has ( base_dir ) ) {
da - > make_dir_recursive ( String ( " android/build " ) . plus_file ( base_dir ) ) ;
dirs_tested . insert ( base_dir ) ;
}
2019-08-27 14:42:15 +00:00
String to_write = String ( " res://android/build " ) . plus_file ( path ) ;
2019-04-07 18:46:52 +00:00
FileAccess * f = FileAccess : : open ( to_write , FileAccess : : WRITE ) ;
if ( f ) {
f - > store_buffer ( data . ptr ( ) , data . size ( ) ) ;
memdelete ( f ) ;
# ifndef WINDOWS_ENABLED
FileAccess : : set_unix_permissions ( to_write , ( info . external_fa > > 16 ) & 0x01FF ) ;
# endif
} else {
2019-05-19 10:34:40 +00:00
ERR_PRINTS ( " Can't uncompress file: " + to_write ) ;
2019-04-07 18:46:52 +00:00
}
}
2019-08-27 14:42:15 +00:00
ProgressDialog : : get_singleton ( ) - > task_step ( " uncompress_src " , path , idx ) ;
idx + + ;
ret = unzGoToNextFile ( pkg ) ;
}
ProgressDialog : : get_singleton ( ) - > end_task ( " uncompress_src " ) ;
unzClose ( pkg ) ;
// Extract libs from pre-built APKs.
err = _extract_libs_from_apk ( " release " ) ;
ERR_FAIL_COND_V_MSG ( err ! = OK , err , " Can't extract Android libs from android_release.apk. " ) ;
err = _extract_libs_from_apk ( " debug " ) ;
ERR_FAIL_COND_V_MSG ( err ! = OK , err , " Can't extract Android libs from android_debug.apk. " ) ;
return OK ;
}
Error ExportTemplateManager : : _extract_libs_from_apk ( const String & p_target_name ) {
const String & templates_path = EditorSettings : : get_singleton ( ) - > get_templates_dir ( ) . plus_file ( VERSION_FULL_CONFIG ) ;
const String & apk_file = templates_path . plus_file ( " android_ " + p_target_name + " .apk " ) ;
ERR_FAIL_COND_V ( ! FileAccess : : exists ( apk_file ) , ERR_CANT_OPEN ) ;
FileAccess * src_f = NULL ;
zlib_filefunc_def io = zipio_create_io_from_file ( & src_f ) ;
unzFile pkg = unzOpen2 ( apk_file . utf8 ( ) . get_data ( ) , & io ) ;
ERR_FAIL_COND_V_MSG ( ! pkg , ERR_CANT_OPEN , " Android APK can't be extracted. " ) ;
DirAccessRef da = DirAccess : : open ( " res:// " ) ;
ERR_FAIL_COND_V ( ! da , ERR_CANT_CREATE ) ;
// 8 steps because 4 arches, 2 libs per arch.
ProgressDialog : : get_singleton ( ) - > add_task ( " extract_libs_from_apk " , TTR ( " Extracting Android Libraries From APKs " ) , 8 ) ;
int ret = unzGoToFirstFile ( pkg ) ;
Set < String > dirs_tested ;
int idx = 0 ;
while ( ret = = UNZ_OK ) {
// Get file path.
unz_file_info info ;
char fpath [ 16384 ] ;
ret = unzGetCurrentFileInfo ( pkg , & info , fpath , 16384 , NULL , 0 , NULL , 0 ) ;
String path = fpath ;
String base_dir = path . get_base_dir ( ) ;
String file = path . get_file ( ) ;
if ( ! base_dir . begins_with ( " lib " ) | | path . ends_with ( " / " ) ) {
ret = unzGoToNextFile ( pkg ) ;
continue ;
}
Vector < uint8_t > data ;
data . resize ( info . uncompressed_size ) ;
// Read.
unzOpenCurrentFile ( pkg ) ;
unzReadCurrentFile ( pkg , data . ptrw ( ) , data . size ( ) ) ;
unzCloseCurrentFile ( pkg ) ;
// We have a "lib" folder in the APK, but it should be "libs/{release,debug}" in the source dir.
String target_base_dir = base_dir . replace_first ( " lib " , String ( " libs " ) . plus_file ( p_target_name ) ) ;
if ( ! dirs_tested . has ( base_dir ) ) {
da - > make_dir_recursive ( String ( " android/build " ) . plus_file ( target_base_dir ) ) ;
dirs_tested . insert ( base_dir ) ;
}
String to_write = String ( " res://android/build " ) . plus_file ( target_base_dir . plus_file ( path . get_file ( ) ) ) ;
FileAccess * f = FileAccess : : open ( to_write , FileAccess : : WRITE ) ;
if ( f ) {
f - > store_buffer ( data . ptr ( ) , data . size ( ) ) ;
memdelete ( f ) ;
# ifndef WINDOWS_ENABLED
// We can't retrieve Unix permissions from the APK it seems, so simply set 0755 as should be.
FileAccess : : set_unix_permissions ( to_write , 0755 ) ;
# endif
} else {
ERR_PRINTS ( " Can't uncompress file: " + to_write ) ;
}
ProgressDialog : : get_singleton ( ) - > task_step ( " extract_libs_from_apk " , path , idx ) ;
2019-04-07 18:46:52 +00:00
idx + + ;
ret = unzGoToNextFile ( pkg ) ;
}
2019-08-27 14:42:15 +00:00
ProgressDialog : : get_singleton ( ) - > end_task ( " extract_libs_from_apk " ) ;
2019-04-07 18:46:52 +00:00
unzClose ( pkg ) ;
return OK ;
}
2017-03-21 02:31:41 +00:00
void ExportTemplateManager : : _bind_methods ( ) {
ClassDB : : bind_method ( " _download_template " , & ExportTemplateManager : : _download_template ) ;
ClassDB : : bind_method ( " _uninstall_template " , & ExportTemplateManager : : _uninstall_template ) ;
ClassDB : : bind_method ( " _uninstall_template_confirm " , & ExportTemplateManager : : _uninstall_template_confirm ) ;
ClassDB : : bind_method ( " _install_from_file " , & ExportTemplateManager : : _install_from_file ) ;
2017-11-02 02:12:28 +00:00
ClassDB : : bind_method ( " _http_download_mirror_completed " , & ExportTemplateManager : : _http_download_mirror_completed ) ;
ClassDB : : bind_method ( " _http_download_templates_completed " , & ExportTemplateManager : : _http_download_templates_completed ) ;
ClassDB : : bind_method ( " _begin_template_download " , & ExportTemplateManager : : _begin_template_download ) ;
2018-02-01 01:15:06 +00:00
ClassDB : : bind_method ( " _window_template_downloader_closed " , & ExportTemplateManager : : _window_template_downloader_closed ) ;
2017-03-21 02:31:41 +00:00
}
ExportTemplateManager : : ExportTemplateManager ( ) {
VBoxContainer * main_vb = memnew ( VBoxContainer ) ;
add_child ( main_vb ) ;
current_hb = memnew ( HBoxContainer ) ;
main_vb - > add_margin_child ( TTR ( " Current Version: " ) , current_hb , false ) ;
installed_scroll = memnew ( ScrollContainer ) ;
main_vb - > add_margin_child ( TTR ( " Installed Versions: " ) , installed_scroll , true ) ;
installed_vb = memnew ( VBoxContainer ) ;
installed_scroll - > add_child ( installed_vb ) ;
installed_scroll - > set_enable_v_scroll ( true ) ;
installed_scroll - > set_enable_h_scroll ( false ) ;
installed_vb - > set_h_size_flags ( SIZE_EXPAND_FILL ) ;
get_cancel ( ) - > set_text ( TTR ( " Close " ) ) ;
get_ok ( ) - > set_text ( TTR ( " Install From File " ) ) ;
remove_confirm = memnew ( ConfirmationDialog ) ;
remove_confirm - > set_title ( TTR ( " Remove Template " ) ) ;
add_child ( remove_confirm ) ;
remove_confirm - > connect ( " confirmed " , this , " _uninstall_template_confirm " ) ;
template_open = memnew ( FileDialog ) ;
2019-03-25 00:54:29 +00:00
template_open - > set_title ( TTR ( " Select Template File " ) ) ;
2017-03-21 02:31:41 +00:00
template_open - > add_filter ( " *.tpz ; Godot Export Templates " ) ;
template_open - > set_access ( FileDialog : : ACCESS_FILESYSTEM ) ;
template_open - > set_mode ( FileDialog : : MODE_OPEN_FILE ) ;
2017-12-18 18:08:27 +00:00
template_open - > connect ( " file_selected " , this , " _install_from_file " , varray ( true ) ) ;
2017-03-21 02:31:41 +00:00
add_child ( template_open ) ;
set_title ( TTR ( " Export Template Manager " ) ) ;
set_hide_on_ok ( false ) ;
2017-11-02 02:12:28 +00:00
request_mirror = memnew ( HTTPRequest ) ;
add_child ( request_mirror ) ;
request_mirror - > connect ( " request_completed " , this , " _http_download_mirror_completed " ) ;
download_templates = memnew ( HTTPRequest ) ;
add_child ( download_templates ) ;
download_templates - > connect ( " request_completed " , this , " _http_download_templates_completed " ) ;
template_downloader = memnew ( AcceptDialog ) ;
template_downloader - > set_title ( TTR ( " Download Templates " ) ) ;
template_downloader - > get_ok ( ) - > set_text ( TTR ( " Close " ) ) ;
2018-02-01 01:15:06 +00:00
template_downloader - > set_exclusive ( true ) ;
2017-11-02 02:12:28 +00:00
add_child ( template_downloader ) ;
2018-02-01 01:15:06 +00:00
template_downloader - > connect ( " popup_hide " , this , " _window_template_downloader_closed " ) ;
2017-11-02 02:12:28 +00:00
VBoxContainer * vbc = memnew ( VBoxContainer ) ;
template_downloader - > add_child ( vbc ) ;
ScrollContainer * sc = memnew ( ScrollContainer ) ;
sc - > set_custom_minimum_size ( Size2 ( 400 , 200 ) * EDSCALE ) ;
2018-01-26 19:38:08 +00:00
vbc - > add_margin_child ( TTR ( " Select mirror from list: (Shift+Click: Open in Browser) " ) , sc ) ;
2017-11-02 02:12:28 +00:00
template_list = memnew ( VBoxContainer ) ;
sc - > add_child ( template_list ) ;
sc - > set_enable_v_scroll ( true ) ;
sc - > set_enable_h_scroll ( false ) ;
template_list_state = memnew ( Label ) ;
vbc - > add_child ( template_list_state ) ;
template_download_progress = memnew ( ProgressBar ) ;
vbc - > add_child ( template_download_progress ) ;
update_countdown = 0 ;
2017-03-21 02:31:41 +00:00
}