2023-01-10 15:26:54 +01:00
/**************************************************************************/
/* export.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
2022-01-17 10:52:32 +01:00
# include "export.h"
2022-11-18 21:12:22 +01:00
# include "core/io/image_loader.h"
2018-09-11 18:13:45 +02:00
# include "core/os/file_access.h"
# include "core/os/os.h"
2017-03-05 14:21:25 +01:00
# include "editor/editor_export.h"
2019-10-04 17:01:13 +02:00
# include "editor/editor_node.h"
2017-12-09 04:31:10 +01:00
# include "editor/editor_settings.h"
2017-06-23 17:03:41 +02:00
# include "platform/windows/logo.gen.h"
2017-02-20 03:19:30 +01:00
2017-12-09 04:31:10 +01:00
class EditorExportPlatformWindows : public EditorExportPlatformPC {
2022-11-18 21:12:22 +01:00
Error _process_icon ( const Ref < EditorExportPreset > & p_preset , const String & p_src_path , const String & p_dst_path ) ;
2022-04-28 09:24:29 +02:00
Error _rcedit_add_data ( const Ref < EditorExportPreset > & p_preset , const String & p_path ) ;
2019-10-04 17:01:13 +02:00
Error _code_sign ( const Ref < EditorExportPreset > & p_preset , const String & p_path ) ;
2017-12-09 04:31:10 +01:00
public :
virtual Error export_project ( const Ref < EditorExportPreset > & p_preset , bool p_debug , const String & p_path , int p_flags = 0 ) ;
2019-10-04 17:01:13 +02:00
virtual Error sign_shared_object ( const Ref < EditorExportPreset > & p_preset , bool p_debug , const String & p_path ) ;
2022-05-04 11:56:00 +02:00
virtual Error modify_template ( const Ref < EditorExportPreset > & p_preset , bool p_debug , const String & p_path , int p_flags ) ;
2022-06-02 10:12:50 +02:00
virtual Error fixup_embedded_pck ( const String & p_path , int64_t p_embedded_start , int64_t p_embedded_size ) ;
2017-12-09 04:31:10 +01:00
virtual void get_export_options ( List < ExportOption > * r_options ) ;
2022-09-19 09:55:24 +02:00
virtual bool get_option_visibility ( const EditorExportPreset * p_preset , const String & p_option , const Map < StringName , Variant > & p_options ) const ;
2022-07-17 16:22:54 +02:00
virtual bool has_valid_export_configuration ( const Ref < EditorExportPreset > & p_preset , String & r_error , bool & r_missing_templates ) const ;
virtual bool has_valid_project_configuration ( const Ref < EditorExportPreset > & p_preset , String & r_error ) const ;
2017-12-09 04:31:10 +01:00
} ;
2022-11-18 21:12:22 +01:00
Error EditorExportPlatformWindows : : _process_icon ( const Ref < EditorExportPreset > & p_preset , const String & p_src_path , const String & p_dst_path ) {
static const uint8_t icon_size [ ] = { 16 , 32 , 48 , 64 , 128 , 0 /*256*/ } ;
struct IconData {
PoolVector < uint8_t > data ;
uint8_t pal_colors = 0 ;
uint16_t planes = 0 ;
uint16_t bpp = 32 ;
} ;
HashMap < uint8_t , IconData > images ;
Error err ;
if ( p_src_path . get_extension ( ) = = " ico " ) {
FileAccess * f = FileAccess : : open ( p_src_path , FileAccess : : READ , & err ) ;
if ( err ! = OK ) {
return err ;
}
// Read ICONDIR.
f - > get_16 ( ) ; // Reserved.
uint16_t icon_type = f - > get_16 ( ) ; // Image type: 1 - ICO.
uint16_t icon_count = f - > get_16 ( ) ; // Number of images.
if ( icon_type ! = 1 ) {
f - > close ( ) ;
memdelete ( f ) ;
ERR_FAIL_V ( ERR_CANT_OPEN ) ;
}
for ( uint16_t i = 0 ; i < icon_count ; i + + ) {
// Read ICONDIRENTRY.
uint16_t w = f - > get_8 ( ) ; // Width in pixels.
uint16_t h = f - > get_8 ( ) ; // Height in pixels.
uint8_t pal_colors = f - > get_8 ( ) ; // Number of colors in the palette (0 - no palette).
f - > get_8 ( ) ; // Reserved.
uint16_t planes = f - > get_16 ( ) ; // Number of color planes.
uint16_t bpp = f - > get_16 ( ) ; // Bits per pixel.
uint32_t img_size = f - > get_32 ( ) ; // Image data size in bytes.
uint32_t img_offset = f - > get_32 ( ) ; // Image data offset.
if ( w ! = h ) {
continue ;
}
// Read image data.
uint64_t prev_offset = f - > get_position ( ) ;
images [ w ] . pal_colors = pal_colors ;
images [ w ] . planes = planes ;
images [ w ] . bpp = bpp ;
images [ w ] . data . resize ( img_size ) ;
PoolVector < uint8_t > : : Write wr = images [ w ] . data . write ( ) ;
f - > seek ( img_offset ) ;
f - > get_buffer ( wr . ptr ( ) , img_size ) ;
f - > seek ( prev_offset ) ;
}
f - > close ( ) ;
memdelete ( f ) ;
} else {
Ref < Image > src_image ;
src_image . instance ( ) ;
err = ImageLoader : : load_image ( p_src_path , src_image . ptr ( ) ) ;
ERR_FAIL_COND_V ( err ! = OK | | src_image - > empty ( ) , ERR_CANT_OPEN ) ;
for ( size_t i = 0 ; i < sizeof ( icon_size ) / sizeof ( icon_size [ 0 ] ) ; + + i ) {
int size = ( icon_size [ i ] = = 0 ) ? 256 : icon_size [ i ] ;
Ref < Image > res_image = src_image - > duplicate ( ) ;
ERR_FAIL_COND_V ( res_image . is_null ( ) | | res_image - > empty ( ) , ERR_CANT_OPEN ) ;
res_image - > resize ( size , size , ( Image : : Interpolation ) ( p_preset - > get ( " application/icon_interpolation " ) . operator int ( ) ) ) ;
images [ icon_size [ i ] ] . data = res_image - > save_png_to_buffer ( ) ;
}
}
uint16_t valid_icon_count = 0 ;
for ( size_t i = 0 ; i < sizeof ( icon_size ) / sizeof ( icon_size [ 0 ] ) ; + + i ) {
if ( images . has ( icon_size [ i ] ) ) {
valid_icon_count + + ;
} else {
int size = ( icon_size [ i ] = = 0 ) ? 256 : icon_size [ i ] ;
add_message ( EXPORT_MESSAGE_WARNING , TTR ( " Resources Modification " ) , vformat ( TTR ( " Icon size \" %d \" is missing. " ) , size ) ) ;
}
}
ERR_FAIL_COND_V ( valid_icon_count = = 0 , ERR_CANT_OPEN ) ;
FileAccess * fw = FileAccess : : open ( p_dst_path , FileAccess : : WRITE , & err ) ;
if ( err ! = OK ) {
return err ;
}
// Write ICONDIR.
fw - > store_16 ( 0 ) ; // Reserved.
fw - > store_16 ( 1 ) ; // Image type: 1 - ICO.
fw - > store_16 ( valid_icon_count ) ; // Number of images.
// Write ICONDIRENTRY.
uint32_t img_offset = 6 + 16 * valid_icon_count ;
for ( size_t i = 0 ; i < sizeof ( icon_size ) / sizeof ( icon_size [ 0 ] ) ; + + i ) {
if ( images . has ( icon_size [ i ] ) ) {
const IconData & di = images [ icon_size [ i ] ] ;
fw - > store_8 ( icon_size [ i ] ) ; // Width in pixels.
fw - > store_8 ( icon_size [ i ] ) ; // Height in pixels.
fw - > store_8 ( di . pal_colors ) ; // Number of colors in the palette (0 - no palette).
fw - > store_8 ( 0 ) ; // Reserved.
fw - > store_16 ( di . planes ) ; // Number of color planes.
fw - > store_16 ( di . bpp ) ; // Bits per pixel.
fw - > store_32 ( di . data . size ( ) ) ; // Image data size in bytes.
fw - > store_32 ( img_offset ) ; // Image data offset.
img_offset + = di . data . size ( ) ;
}
}
// Write image data.
for ( size_t i = 0 ; i < sizeof ( icon_size ) / sizeof ( icon_size [ 0 ] ) ; + + i ) {
if ( images . has ( icon_size [ i ] ) ) {
const IconData & di = images [ icon_size [ i ] ] ;
PoolVector < uint8_t > : : Read r = di . data . read ( ) ;
fw - > store_buffer ( r . ptr ( ) , di . data . size ( ) ) ;
}
}
fw - > close ( ) ;
memdelete ( fw ) ;
return OK ;
}
2019-10-04 17:01:13 +02:00
Error EditorExportPlatformWindows : : sign_shared_object ( const Ref < EditorExportPreset > & p_preset , bool p_debug , const String & p_path ) {
if ( p_preset - > get ( " codesign/enable " ) ) {
return _code_sign ( p_preset , p_path ) ;
} else {
return OK ;
}
}
2022-05-04 11:56:00 +02:00
Error EditorExportPlatformWindows : : modify_template ( const Ref < EditorExportPreset > & p_preset , bool p_debug , const String & p_path , int p_flags ) {
if ( p_preset - > get ( " application/modify_resources " ) ) {
2022-06-02 10:12:50 +02:00
_rcedit_add_data ( p_preset , p_path ) ;
2022-05-04 11:56:00 +02:00
}
2022-06-02 10:12:50 +02:00
return OK ;
2022-05-04 11:56:00 +02:00
}
2017-12-09 04:31:10 +01:00
Error EditorExportPlatformWindows : : export_project ( const Ref < EditorExportPreset > & p_preset , bool p_debug , const String & p_path , int p_flags ) {
2022-04-28 09:24:29 +02:00
String pck_path = p_path ;
if ( p_preset - > get ( " binary_format/embed_pck " ) ) {
pck_path = p_path . get_basename ( ) + " .tmp " ;
}
2017-12-09 04:31:10 +01:00
2022-05-04 11:56:00 +02:00
Error err = EditorExportPlatformPC : : export_project ( p_preset , p_debug , pck_path , p_flags ) ;
2019-12-03 13:10:18 +01:00
if ( p_preset - > get ( " codesign/enable " ) & & err = = OK ) {
2022-06-02 10:12:50 +02:00
_code_sign ( p_preset , pck_path ) ;
2022-04-28 09:24:29 +02:00
}
if ( p_preset - > get ( " binary_format/embed_pck " ) & & err = = OK ) {
DirAccessRef tmp_dir = DirAccess : : create_for_path ( p_path . get_base_dir ( ) ) ;
err = tmp_dir - > rename ( pck_path , p_path ) ;
2022-06-02 10:12:50 +02:00
if ( err ! = OK ) {
add_message ( EXPORT_MESSAGE_ERROR , TTR ( " PCK Embedding " ) , vformat ( TTR ( " Failed to rename temporary file \" %s \" . " ) , pck_path ) ) ;
}
2019-12-03 13:10:18 +01:00
}
return err ;
}
2022-09-19 09:55:24 +02:00
bool EditorExportPlatformWindows : : get_option_visibility ( const EditorExportPreset * p_preset , const String & p_option , const Map < StringName , Variant > & p_options ) const {
2022-01-25 20:18:22 +01:00
// This option is not supported by "osslsigncode", used on non-Windows host.
if ( ! OS : : get_singleton ( ) - > has_feature ( " Windows " ) & & p_option = = " codesign/identity_type " ) {
return false ;
}
return true ;
}
2019-12-03 13:10:18 +01:00
void EditorExportPlatformWindows : : get_export_options ( List < ExportOption > * r_options ) {
EditorExportPlatformPC : : get_export_options ( r_options ) ;
r_options - > push_back ( ExportOption ( PropertyInfo ( Variant : : BOOL , " codesign/enable " ) , false ) ) ;
r_options - > push_back ( ExportOption ( PropertyInfo ( Variant : : INT , " codesign/identity_type " , PROPERTY_HINT_ENUM , " Select automatically,Use PKCS12 file (specify *.PFX/*.P12 file),Use certificate store (specify SHA1 hash) " ) , 0 ) ) ;
r_options - > push_back ( ExportOption ( PropertyInfo ( Variant : : STRING , " codesign/identity " , PROPERTY_HINT_GLOBAL_FILE , " *.pfx,*.p12 " ) , " " ) ) ;
r_options - > push_back ( ExportOption ( PropertyInfo ( Variant : : STRING , " codesign/password " ) , " " ) ) ;
r_options - > push_back ( ExportOption ( PropertyInfo ( Variant : : BOOL , " codesign/timestamp " ) , true ) ) ;
r_options - > push_back ( ExportOption ( PropertyInfo ( Variant : : STRING , " codesign/timestamp_server_url " ) , " " ) ) ;
r_options - > push_back ( ExportOption ( PropertyInfo ( Variant : : INT , " codesign/digest_algorithm " , PROPERTY_HINT_ENUM , " SHA1,SHA256 " ) , 1 ) ) ;
r_options - > push_back ( ExportOption ( PropertyInfo ( Variant : : STRING , " codesign/description " ) , " " ) ) ;
r_options - > push_back ( ExportOption ( PropertyInfo ( Variant : : POOL_STRING_ARRAY , " codesign/custom_options " ) , PoolStringArray ( ) ) ) ;
2022-04-28 09:24:29 +02:00
r_options - > push_back ( ExportOption ( PropertyInfo ( Variant : : BOOL , " application/modify_resources " ) , true ) ) ;
2022-11-18 21:12:22 +01:00
r_options - > push_back ( ExportOption ( PropertyInfo ( Variant : : STRING , " application/icon " , PROPERTY_HINT_FILE , " *.ico,*.png,*.webp,*.svg " ) , " " ) ) ;
r_options - > push_back ( ExportOption ( PropertyInfo ( Variant : : INT , " application/icon_interpolation " , PROPERTY_HINT_ENUM , " Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos " ) , 4 ) ) ;
2022-02-03 09:25:00 +01:00
r_options - > push_back ( ExportOption ( PropertyInfo ( Variant : : STRING , " application/file_version " , PROPERTY_HINT_PLACEHOLDER_TEXT , " 1.0.0.0 " ) , " " ) ) ;
r_options - > push_back ( ExportOption ( PropertyInfo ( Variant : : STRING , " application/product_version " , PROPERTY_HINT_PLACEHOLDER_TEXT , " 1.0.0.0 " ) , " " ) ) ;
2019-12-03 13:10:18 +01:00
r_options - > push_back ( ExportOption ( PropertyInfo ( Variant : : STRING , " application/company_name " , PROPERTY_HINT_PLACEHOLDER_TEXT , " Company Name " ) , " " ) ) ;
r_options - > push_back ( ExportOption ( PropertyInfo ( Variant : : STRING , " application/product_name " , PROPERTY_HINT_PLACEHOLDER_TEXT , " Game Name " ) , " " ) ) ;
r_options - > push_back ( ExportOption ( PropertyInfo ( Variant : : STRING , " application/file_description " ) , " " ) ) ;
r_options - > push_back ( ExportOption ( PropertyInfo ( Variant : : STRING , " application/copyright " ) , " " ) ) ;
r_options - > push_back ( ExportOption ( PropertyInfo ( Variant : : STRING , " application/trademarks " ) , " " ) ) ;
}
2022-04-28 09:24:29 +02:00
Error EditorExportPlatformWindows : : _rcedit_add_data ( const Ref < EditorExportPreset > & p_preset , const String & p_path ) {
2017-12-09 04:31:10 +01:00
String rcedit_path = EditorSettings : : get_singleton ( ) - > get ( " export/windows/rcedit " ) ;
2017-12-09 11:41:56 +01:00
2022-04-28 09:24:29 +02:00
if ( rcedit_path ! = String ( ) & & ! FileAccess : : exists ( rcedit_path ) ) {
2022-06-02 10:12:50 +02:00
add_message ( EXPORT_MESSAGE_WARNING , TTR ( " Resources Modification " ) , vformat ( TTR ( " Could not find rcedit executable at \" %s \" . " ) , rcedit_path ) ) ;
2022-04-28 09:24:29 +02:00
return ERR_FILE_NOT_FOUND ;
2017-12-09 11:41:56 +01:00
}
2022-04-28 09:24:29 +02:00
if ( rcedit_path = = String ( ) ) {
rcedit_path = " rcedit " ; // try to run rcedit from PATH
2017-12-09 11:41:56 +01:00
}
# ifndef WINDOWS_ENABLED
// On non-Windows we need WINE to run rcedit
String wine_path = EditorSettings : : get_singleton ( ) - > get ( " export/windows/wine " ) ;
if ( wine_path ! = String ( ) & & ! FileAccess : : exists ( wine_path ) ) {
2022-06-02 10:12:50 +02:00
add_message ( EXPORT_MESSAGE_WARNING , TTR ( " Resources Modification " ) , vformat ( TTR ( " Could not find wine executable at \" %s \" . " ) , wine_path ) ) ;
2022-04-28 09:24:29 +02:00
return ERR_FILE_NOT_FOUND ;
2017-12-09 11:41:56 +01:00
}
if ( wine_path = = String ( ) ) {
wine_path = " wine " ; // try to run wine from PATH
}
# endif
2019-02-02 16:54:42 +01:00
String icon_path = ProjectSettings : : get_singleton ( ) - > globalize_path ( p_preset - > get ( " application/icon " ) ) ;
2022-11-18 21:12:22 +01:00
String tmp_icon_path = EditorSettings : : get_singleton ( ) - > get_cache_dir ( ) . plus_file ( " _rcedit.ico " ) ;
if ( ! icon_path . empty ( ) ) {
if ( _process_icon ( p_preset , icon_path , tmp_icon_path ) ! = OK ) {
add_message ( EXPORT_MESSAGE_WARNING , TTR ( " Resources Modification " ) , vformat ( TTR ( " Invalid icon file \" %s \" . " ) , icon_path ) ) ;
icon_path = String ( ) ;
}
}
2017-12-09 04:31:10 +01:00
String file_verion = p_preset - > get ( " application/file_version " ) ;
String product_version = p_preset - > get ( " application/product_version " ) ;
String company_name = p_preset - > get ( " application/company_name " ) ;
String product_name = p_preset - > get ( " application/product_name " ) ;
String file_description = p_preset - > get ( " application/file_description " ) ;
String copyright = p_preset - > get ( " application/copyright " ) ;
String trademarks = p_preset - > get ( " application/trademarks " ) ;
String comments = p_preset - > get ( " application/comments " ) ;
List < String > args ;
args . push_back ( p_path ) ;
if ( icon_path ! = String ( ) ) {
args . push_back ( " --set-icon " ) ;
2022-11-18 21:12:22 +01:00
args . push_back ( tmp_icon_path ) ;
2017-12-09 04:31:10 +01:00
}
if ( file_verion ! = String ( ) ) {
args . push_back ( " --set-file-version " ) ;
args . push_back ( file_verion ) ;
}
if ( product_version ! = String ( ) ) {
args . push_back ( " --set-product-version " ) ;
args . push_back ( product_version ) ;
}
if ( company_name ! = String ( ) ) {
args . push_back ( " --set-version-string " ) ;
args . push_back ( " CompanyName " ) ;
args . push_back ( company_name ) ;
}
if ( product_name ! = String ( ) ) {
args . push_back ( " --set-version-string " ) ;
args . push_back ( " ProductName " ) ;
args . push_back ( product_name ) ;
}
if ( file_description ! = String ( ) ) {
args . push_back ( " --set-version-string " ) ;
args . push_back ( " FileDescription " ) ;
args . push_back ( file_description ) ;
}
if ( copyright ! = String ( ) ) {
args . push_back ( " --set-version-string " ) ;
args . push_back ( " LegalCopyright " ) ;
args . push_back ( copyright ) ;
}
if ( trademarks ! = String ( ) ) {
args . push_back ( " --set-version-string " ) ;
args . push_back ( " LegalTrademarks " ) ;
args . push_back ( trademarks ) ;
}
2022-04-28 09:24:29 +02:00
# ifndef WINDOWS_ENABLED
2017-12-09 11:41:56 +01:00
// On non-Windows we need WINE to run rcedit
args . push_front ( rcedit_path ) ;
2022-04-28 09:24:29 +02:00
rcedit_path = wine_path ;
2017-12-09 11:41:56 +01:00
# endif
2022-04-28 09:24:29 +02:00
String str ;
Error err = OS : : get_singleton ( ) - > execute ( rcedit_path , args , true , nullptr , & str , nullptr , true ) ;
2022-11-18 21:12:22 +01:00
if ( FileAccess : : exists ( tmp_icon_path ) ) {
DirAccess : : remove_file_or_error ( tmp_icon_path ) ;
}
2022-06-02 10:12:50 +02:00
if ( err ! = OK | | ( str . find ( " not found " ) ! = - 1 ) | | ( str . find ( " not recognized " ) ! = - 1 ) ) {
2022-09-19 09:55:24 +02:00
add_message ( EXPORT_MESSAGE_WARNING , TTR ( " Resources Modification " ) , TTR ( " Could not start rcedit executable. Configure rcedit path in the Editor Settings (Export > Windows > rcedit), or disable \" Application > Modify Resources \" in the export preset. " ) ) ;
2022-06-02 10:12:50 +02:00
return err ;
}
2022-04-28 09:24:29 +02:00
print_line ( " rcedit ( " + p_path + " ): " + str ) ;
if ( str . find ( " Fatal error " ) ! = - 1 ) {
2022-07-29 21:31:39 +02:00
add_message ( EXPORT_MESSAGE_WARNING , TTR ( " Resources Modification " ) , vformat ( TTR ( " rcedit failed to modify executable: %s. " ) , str ) ) ;
2022-04-28 09:24:29 +02:00
return FAILED ;
}
return OK ;
2017-12-09 04:31:10 +01:00
}
2019-10-04 17:01:13 +02:00
Error EditorExportPlatformWindows : : _code_sign ( const Ref < EditorExportPreset > & p_preset , const String & p_path ) {
List < String > args ;
# ifdef WINDOWS_ENABLED
String signtool_path = EditorSettings : : get_singleton ( ) - > get ( " export/windows/signtool " ) ;
if ( signtool_path ! = String ( ) & & ! FileAccess : : exists ( signtool_path ) ) {
2022-06-02 10:12:50 +02:00
add_message ( EXPORT_MESSAGE_WARNING , TTR ( " Code Signing " ) , vformat ( TTR ( " Could not find signtool executable at \" %s \" . " ) , signtool_path ) ) ;
2019-10-04 17:01:13 +02:00
return ERR_FILE_NOT_FOUND ;
}
if ( signtool_path = = String ( ) ) {
signtool_path = " signtool " ; // try to run signtool from PATH
}
# else
String signtool_path = EditorSettings : : get_singleton ( ) - > get ( " export/windows/osslsigncode " ) ;
if ( signtool_path ! = String ( ) & & ! FileAccess : : exists ( signtool_path ) ) {
2022-06-02 10:12:50 +02:00
add_message ( EXPORT_MESSAGE_WARNING , TTR ( " Code Signing " ) , vformat ( TTR ( " Could not find osslsigncode executable at \" %s \" . " ) , signtool_path ) ) ;
2019-10-04 17:01:13 +02:00
return ERR_FILE_NOT_FOUND ;
}
if ( signtool_path = = String ( ) ) {
signtool_path = " osslsigncode " ; // try to run signtool from PATH
}
# endif
args . push_back ( " sign " ) ;
//identity
# ifdef WINDOWS_ENABLED
int id_type = p_preset - > get ( " codesign/identity_type " ) ;
if ( id_type = = 0 ) { //auto select
args . push_back ( " /a " ) ;
} else if ( id_type = = 1 ) { //pkcs12
if ( p_preset - > get ( " codesign/identity " ) ! = " " ) {
args . push_back ( " /f " ) ;
args . push_back ( p_preset - > get ( " codesign/identity " ) ) ;
} else {
2022-06-02 10:12:50 +02:00
add_message ( EXPORT_MESSAGE_WARNING , TTR ( " Code Signing " ) , TTR ( " No identity found. " ) ) ;
2019-10-04 17:01:13 +02:00
return FAILED ;
}
} else if ( id_type = = 2 ) { //Windows certificate store
if ( p_preset - > get ( " codesign/identity " ) ! = " " ) {
args . push_back ( " /sha1 " ) ;
args . push_back ( p_preset - > get ( " codesign/identity " ) ) ;
} else {
2022-06-02 10:12:50 +02:00
add_message ( EXPORT_MESSAGE_WARNING , TTR ( " Code Signing " ) , TTR ( " No identity found. " ) ) ;
2019-10-04 17:01:13 +02:00
return FAILED ;
}
} else {
2022-06-02 10:12:50 +02:00
add_message ( EXPORT_MESSAGE_WARNING , TTR ( " Code Signing " ) , TTR ( " Invalid identity type. " ) ) ;
2019-10-04 17:01:13 +02:00
return FAILED ;
}
# else
if ( p_preset - > get ( " codesign/identity " ) ! = " " ) {
args . push_back ( " -pkcs12 " ) ;
args . push_back ( p_preset - > get ( " codesign/identity " ) ) ;
} else {
2022-06-02 10:12:50 +02:00
add_message ( EXPORT_MESSAGE_WARNING , TTR ( " Code Signing " ) , TTR ( " No identity found. " ) ) ;
2019-10-04 17:01:13 +02:00
return FAILED ;
}
# endif
//password
if ( p_preset - > get ( " codesign/password " ) ! = " " ) {
# ifdef WINDOWS_ENABLED
args . push_back ( " /p " ) ;
# else
args . push_back ( " -pass " ) ;
# endif
args . push_back ( p_preset - > get ( " codesign/password " ) ) ;
}
//timestamp
if ( p_preset - > get ( " codesign/timestamp " ) ) {
if ( p_preset - > get ( " codesign/timestamp_server " ) ! = " " ) {
# ifdef WINDOWS_ENABLED
args . push_back ( " /tr " ) ;
args . push_back ( p_preset - > get ( " codesign/timestamp_server_url " ) ) ;
args . push_back ( " /td " ) ;
if ( ( int ) p_preset - > get ( " codesign/digest_algorithm " ) = = 0 ) {
args . push_back ( " sha1 " ) ;
} else {
args . push_back ( " sha256 " ) ;
}
# else
args . push_back ( " -ts " ) ;
args . push_back ( p_preset - > get ( " codesign/timestamp_server_url " ) ) ;
# endif
} else {
2022-06-02 10:12:50 +02:00
add_message ( EXPORT_MESSAGE_WARNING , TTR ( " Code Signing " ) , TTR ( " Invalid timestamp server. " ) ) ;
2019-10-04 17:01:13 +02:00
return FAILED ;
}
}
//digest
# ifdef WINDOWS_ENABLED
args . push_back ( " /fd " ) ;
# else
args . push_back ( " -h " ) ;
# endif
if ( ( int ) p_preset - > get ( " codesign/digest_algorithm " ) = = 0 ) {
args . push_back ( " sha1 " ) ;
} else {
args . push_back ( " sha256 " ) ;
}
//description
if ( p_preset - > get ( " codesign/description " ) ! = " " ) {
# ifdef WINDOWS_ENABLED
args . push_back ( " /d " ) ;
# else
args . push_back ( " -n " ) ;
# endif
args . push_back ( p_preset - > get ( " codesign/description " ) ) ;
}
//user options
PoolStringArray user_args = p_preset - > get ( " codesign/custom_options " ) ;
for ( int i = 0 ; i < user_args . size ( ) ; i + + ) {
String user_arg = user_args [ i ] . strip_edges ( ) ;
if ( ! user_arg . empty ( ) ) {
args . push_back ( user_arg ) ;
}
}
# ifndef WINDOWS_ENABLED
args . push_back ( " -in " ) ;
# endif
args . push_back ( p_path ) ;
# ifndef WINDOWS_ENABLED
args . push_back ( " -out " ) ;
2021-06-29 09:38:28 +02:00
args . push_back ( p_path + " _signed " ) ;
2019-10-04 17:01:13 +02:00
# endif
String str ;
2021-05-04 16:00:45 +02:00
Error err = OS : : get_singleton ( ) - > execute ( signtool_path , args , true , nullptr , & str , nullptr , true ) ;
2022-06-02 10:12:50 +02:00
if ( err ! = OK | | ( str . find ( " not found " ) ! = - 1 ) | | ( str . find ( " not recognized " ) ! = - 1 ) ) {
2022-09-19 09:55:24 +02:00
# ifndef WINDOWS_ENABLED
add_message ( EXPORT_MESSAGE_WARNING , TTR ( " Code Signing " ) , TTR ( " Could not start signtool executable. Configure signtool path in the Editor Settings (Export > Windows > signtool), or disable \" Codesign \" in the export preset. " ) ) ;
# else
add_message ( EXPORT_MESSAGE_WARNING , TTR ( " Code Signing " ) , TTR ( " Could not start osslsigncode executable. Configure signtool path in the Editor Settings (Export > Windows > osslsigncode), or disable \" Codesign \" in the export preset. " ) ) ;
# endif
2022-06-02 10:12:50 +02:00
return err ;
}
2019-10-04 17:01:13 +02:00
print_line ( " codesign ( " + p_path + " ): " + str ) ;
# ifndef WINDOWS_ENABLED
if ( str . find ( " SignTool Error " ) ! = - 1 ) {
# else
if ( str . find ( " Failed " ) ! = - 1 ) {
# endif
2022-07-29 21:31:39 +02:00
add_message ( EXPORT_MESSAGE_WARNING , TTR ( " Code Signing " ) , vformat ( TTR ( " Signtool failed to sign executable: %s. " ) , str ) ) ;
2019-10-04 17:01:13 +02:00
return FAILED ;
}
2021-06-29 09:38:28 +02:00
# ifndef WINDOWS_ENABLED
DirAccessRef tmp_dir = DirAccess : : create_for_path ( p_path . get_base_dir ( ) ) ;
err = tmp_dir - > remove ( p_path ) ;
2022-06-02 10:12:50 +02:00
if ( err ! = OK ) {
add_message ( EXPORT_MESSAGE_WARNING , TTR ( " Code Signing " ) , vformat ( TTR ( " Failed to remove temporary file \" %s \" . " ) , p_path ) ) ;
return err ;
}
2021-06-29 09:38:28 +02:00
err = tmp_dir - > rename ( p_path + " _signed " , p_path ) ;
2022-06-02 10:12:50 +02:00
if ( err ! = OK ) {
add_message ( EXPORT_MESSAGE_WARNING , TTR ( " Code Signing " ) , vformat ( TTR ( " Failed to rename temporary file \" %s \" . " ) , p_path + " _signed " ) ) ;
return err ;
}
2021-06-29 09:38:28 +02:00
# endif
2019-10-04 17:01:13 +02:00
return OK ;
}
2022-07-17 16:22:54 +02:00
bool EditorExportPlatformWindows : : has_valid_export_configuration ( const Ref < EditorExportPreset > & p_preset , String & r_error , bool & r_missing_templates ) const {
2022-02-03 09:25:00 +01:00
String err = " " ;
2022-07-17 16:22:54 +02:00
bool valid = EditorExportPlatformPC : : has_valid_export_configuration ( p_preset , err , r_missing_templates ) ;
2022-02-03 09:25:00 +01:00
String rcedit_path = EditorSettings : : get_singleton ( ) - > get ( " export/windows/rcedit " ) ;
2022-04-28 09:24:29 +02:00
if ( p_preset - > get ( " application/modify_resources " ) & & rcedit_path . empty ( ) ) {
2022-09-19 09:55:24 +02:00
err + = TTR ( " The rcedit tool must be configured in the Editor Settings (Export > Windows > rcedit) to change the icon or app information data. " ) + " \n " ;
2022-02-03 09:25:00 +01:00
}
2022-07-17 16:22:54 +02:00
if ( ! err . empty ( ) ) {
r_error = err ;
}
return valid ;
}
bool EditorExportPlatformWindows : : has_valid_project_configuration ( const Ref < EditorExportPreset > & p_preset , String & r_error ) const {
String err = " " ;
bool valid = true ;
2022-02-03 09:25:00 +01:00
String icon_path = ProjectSettings : : get_singleton ( ) - > globalize_path ( p_preset - > get ( " application/icon " ) ) ;
if ( ! icon_path . empty ( ) & & ! FileAccess : : exists ( icon_path ) ) {
err + = TTR ( " Invalid icon path: " ) + " " + icon_path + " \n " ;
}
// Only non-negative integers can exist in the version string.
String file_version = p_preset - > get ( " application/file_version " ) ;
if ( ! file_version . empty ( ) ) {
Vector < String > version_array = file_version . split ( " . " , false ) ;
if ( version_array . size ( ) ! = 4 | | ! version_array [ 0 ] . is_valid_integer ( ) | |
! version_array [ 1 ] . is_valid_integer ( ) | | ! version_array [ 2 ] . is_valid_integer ( ) | |
! version_array [ 3 ] . is_valid_integer ( ) | | file_version . find ( " - " ) > - 1 ) {
err + = TTR ( " Invalid file version: " ) + " " + file_version + " \n " ;
}
}
String product_version = p_preset - > get ( " application/product_version " ) ;
if ( ! product_version . empty ( ) ) {
Vector < String > version_array = product_version . split ( " . " , false ) ;
if ( version_array . size ( ) ! = 4 | | ! version_array [ 0 ] . is_valid_integer ( ) | |
! version_array [ 1 ] . is_valid_integer ( ) | | ! version_array [ 2 ] . is_valid_integer ( ) | |
! version_array [ 3 ] . is_valid_integer ( ) | | product_version . find ( " - " ) > - 1 ) {
err + = TTR ( " Invalid product version: " ) + " " + product_version + " \n " ;
}
}
if ( ! err . empty ( ) ) {
r_error = err ;
}
return valid ;
}
2022-06-02 10:12:50 +02:00
Error EditorExportPlatformWindows : : fixup_embedded_pck ( const String & p_path , int64_t p_embedded_start , int64_t p_embedded_size ) {
2019-03-25 20:56:33 +01:00
// Patch the header of the "pck" section in the PE file so that it corresponds to the embedded data
2022-04-28 09:24:29 +02:00
if ( p_embedded_size + p_embedded_start > = 0x100000000 ) { // Check for total executable size
2022-06-02 10:12:50 +02:00
add_message ( EXPORT_MESSAGE_ERROR , TTR ( " PCK Embedding " ) , TTR ( " Windows executables cannot be >= 4 GiB. " ) ) ;
return ERR_INVALID_DATA ;
2022-04-28 09:24:29 +02:00
}
2019-03-25 20:56:33 +01:00
FileAccess * f = FileAccess : : open ( p_path , FileAccess : : READ_WRITE ) ;
if ( ! f ) {
2022-06-02 10:12:50 +02:00
add_message ( EXPORT_MESSAGE_ERROR , TTR ( " PCK Embedding " ) , vformat ( TTR ( " Failed to open executable file \" %s \" . " ) , p_path ) ) ;
2019-03-25 20:56:33 +01:00
return ERR_CANT_OPEN ;
}
// Jump to the PE header and check the magic number
{
f - > seek ( 0x3c ) ;
uint32_t pe_pos = f - > get_32 ( ) ;
f - > seek ( pe_pos ) ;
uint32_t magic = f - > get_32 ( ) ;
if ( magic ! = 0x00004550 ) {
f - > close ( ) ;
2022-06-02 10:12:50 +02:00
add_message ( EXPORT_MESSAGE_ERROR , TTR ( " PCK Embedding " ) , TTR ( " Executable file header corrupted. " ) ) ;
2019-03-25 20:56:33 +01:00
return ERR_FILE_CORRUPT ;
}
}
// Process header
int num_sections ;
{
int64_t header_pos = f - > get_position ( ) ;
f - > seek ( header_pos + 2 ) ;
num_sections = f - > get_16 ( ) ;
f - > seek ( header_pos + 16 ) ;
uint16_t opt_header_size = f - > get_16 ( ) ;
// Skip rest of header + optional header to go to the section headers
f - > seek ( f - > get_position ( ) + 2 + opt_header_size ) ;
}
// Search for the "pck" section
int64_t section_table_pos = f - > get_position ( ) ;
bool found = false ;
for ( int i = 0 ; i < num_sections ; + + i ) {
int64_t section_header_pos = section_table_pos + i * 40 ;
f - > seek ( section_header_pos ) ;
uint8_t section_name [ 9 ] ;
f - > get_buffer ( section_name , 8 ) ;
section_name [ 8 ] = ' \0 ' ;
if ( strcmp ( ( char * ) section_name , " pck " ) = = 0 ) {
// "pck" section found, let's patch!
// Set virtual size to a little to avoid it taking memory (zero would give issues)
f - > seek ( section_header_pos + 8 ) ;
f - > store_32 ( 8 ) ;
f - > seek ( section_header_pos + 16 ) ;
f - > store_32 ( p_embedded_size ) ;
f - > seek ( section_header_pos + 20 ) ;
f - > store_32 ( p_embedded_start ) ;
found = true ;
break ;
}
}
f - > close ( ) ;
2022-06-02 10:12:50 +02:00
if ( ! found ) {
add_message ( EXPORT_MESSAGE_ERROR , TTR ( " PCK Embedding " ) , TTR ( " Executable \" pck \" section not found. " ) ) ;
return ERR_FILE_CORRUPT ;
}
return OK ;
}
void register_windows_exporter ( ) {
2022-07-13 19:39:08 +02:00
# ifndef ANDROID_ENABLED
2022-06-02 10:12:50 +02:00
EDITOR_DEF ( " export/windows/rcedit " , " " ) ;
EditorSettings : : get_singleton ( ) - > add_property_hint ( PropertyInfo ( Variant : : STRING , " export/windows/rcedit " , PROPERTY_HINT_GLOBAL_FILE , " *.exe " ) ) ;
# ifdef WINDOWS_ENABLED
EDITOR_DEF ( " export/windows/signtool " , " " ) ;
EditorSettings : : get_singleton ( ) - > add_property_hint ( PropertyInfo ( Variant : : STRING , " export/windows/signtool " , PROPERTY_HINT_GLOBAL_FILE , " *.exe " ) ) ;
# else
EDITOR_DEF ( " export/windows/osslsigncode " , " " ) ;
EditorSettings : : get_singleton ( ) - > add_property_hint ( PropertyInfo ( Variant : : STRING , " export/windows/osslsigncode " , PROPERTY_HINT_GLOBAL_FILE ) ) ;
// On non-Windows we need WINE to run rcedit
EDITOR_DEF ( " export/windows/wine " , " " ) ;
EditorSettings : : get_singleton ( ) - > add_property_hint ( PropertyInfo ( Variant : : STRING , " export/windows/wine " , PROPERTY_HINT_GLOBAL_FILE ) ) ;
2022-07-13 19:39:08 +02:00
# endif
2022-06-02 10:12:50 +02:00
# endif
Ref < EditorExportPlatformWindows > platform ;
platform . instance ( ) ;
Ref < Image > img = memnew ( Image ( _windows_logo ) ) ;
Ref < ImageTexture > logo ;
logo . instance ( ) ;
logo - > create_from_image ( img ) ;
platform - > set_logo ( logo ) ;
platform - > set_name ( " Windows Desktop " ) ;
platform - > set_extension ( " exe " ) ;
platform - > set_release_32 ( " windows_32_release.exe " ) ;
platform - > set_debug_32 ( " windows_32_debug.exe " ) ;
platform - > set_release_64 ( " windows_64_release.exe " ) ;
platform - > set_debug_64 ( " windows_64_debug.exe " ) ;
platform - > set_os_name ( " Windows " ) ;
EditorExport : : get_singleton ( ) - > add_export_platform ( platform ) ;
2019-03-25 20:56:33 +01:00
}