2014-02-10 01:10:30 +00:00
/*************************************************************************/
/* config_file.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 12:16:55 +00:00
/* https://godotengine.org */
2014-02-10 01:10:30 +00:00
/*************************************************************************/
2020-01-01 10:16:22 +00:00
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
2014-02-10 01:10:30 +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
2014-02-10 01:10:30 +00:00
# include "config_file.h"
2018-09-11 16:13:45 +00:00
2019-06-27 11:57:45 +00:00
# include "core/io/file_access_encrypted.h"
2018-09-11 16:13:45 +00:00
# include "core/os/keyboard.h"
# include "core/variant_parser.h"
2014-02-10 01:10:30 +00:00
2020-02-17 21:06:54 +00:00
PackedStringArray ConfigFile : : _get_sections ( ) const {
2014-02-10 01:10:30 +00:00
List < String > s ;
get_sections ( & s ) ;
2020-02-17 21:06:54 +00:00
PackedStringArray arr ;
2014-02-10 01:10:30 +00:00
arr . resize ( s . size ( ) ) ;
2017-03-05 15:44:50 +00:00
int idx = 0 ;
for ( const List < String > : : Element * E = s . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
arr . set ( idx + + , E - > get ( ) ) ;
2014-02-10 01:10:30 +00:00
}
return arr ;
}
2020-02-17 21:06:54 +00:00
PackedStringArray ConfigFile : : _get_section_keys ( const String & p_section ) const {
2014-02-10 01:10:30 +00:00
List < String > s ;
2017-03-05 15:44:50 +00:00
get_section_keys ( p_section , & s ) ;
2020-02-17 21:06:54 +00:00
PackedStringArray arr ;
2014-02-10 01:10:30 +00:00
arr . resize ( s . size ( ) ) ;
2017-03-05 15:44:50 +00:00
int idx = 0 ;
for ( const List < String > : : Element * E = s . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
arr . set ( idx + + , E - > get ( ) ) ;
2014-02-10 01:10:30 +00:00
}
return arr ;
}
2017-03-05 15:44:50 +00:00
void ConfigFile : : set_value ( const String & p_section , const String & p_key , const Variant & p_value ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( p_value . get_type ( ) = = Variant : : NIL ) {
2014-02-10 01:10:30 +00:00
//erase
if ( ! values . has ( p_section ) )
return ; // ?
values [ p_section ] . erase ( p_key ) ;
if ( values [ p_section ] . empty ( ) ) {
values . erase ( p_section ) ;
}
} else {
if ( ! values . has ( p_section ) ) {
2017-11-03 13:33:19 +00:00
values [ p_section ] = OrderedHashMap < String , Variant > ( ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
values [ p_section ] [ p_key ] = p_value ;
2014-02-10 01:10:30 +00:00
}
}
2017-03-05 15:44:50 +00:00
Variant ConfigFile : : get_value ( const String & p_section , const String & p_key , Variant p_default ) const {
2014-02-10 01:10:30 +00:00
2017-03-21 11:32:44 +00:00
if ( ! values . has ( p_section ) | | ! values [ p_section ] . has ( p_key ) ) {
2019-09-27 02:16:44 +00:00
ERR_FAIL_COND_V_MSG ( p_default . get_type ( ) = = Variant : : NIL , Variant ( ) , " Couldn't find the given section ' " + p_section + " ', key ' " + p_key + " ' and no default was given. " ) ;
2017-03-21 11:32:44 +00:00
return p_default ;
}
2019-09-27 02:16:44 +00:00
2014-02-10 01:10:30 +00:00
return values [ p_section ] [ p_key ] ;
}
2017-03-05 15:44:50 +00:00
bool ConfigFile : : has_section ( const String & p_section ) const {
2014-02-10 01:10:30 +00:00
return values . has ( p_section ) ;
}
2017-03-05 15:44:50 +00:00
bool ConfigFile : : has_section_key ( const String & p_section , const String & p_key ) const {
2014-02-10 01:10:30 +00:00
if ( ! values . has ( p_section ) )
return false ;
return values [ p_section ] . has ( p_key ) ;
}
2017-03-05 15:44:50 +00:00
void ConfigFile : : get_sections ( List < String > * r_sections ) const {
2014-02-10 01:10:30 +00:00
2017-11-04 13:08:21 +00:00
for ( OrderedHashMap < String , OrderedHashMap < String , Variant > > : : ConstElement E = values . front ( ) ; E ; E = E . next ( ) ) {
r_sections - > push_back ( E . key ( ) ) ;
2014-02-10 01:10:30 +00:00
}
}
2017-03-05 15:44:50 +00:00
void ConfigFile : : get_section_keys ( const String & p_section , List < String > * r_keys ) const {
2014-02-10 01:10:30 +00:00
2019-09-25 08:28:50 +00:00
ERR_FAIL_COND_MSG ( ! values . has ( p_section ) , " Cannont get keys from nonexistent section ' " + p_section + " '. " ) ;
2014-02-10 01:10:30 +00:00
2017-11-03 13:33:19 +00:00
for ( OrderedHashMap < String , Variant > : : ConstElement E = values [ p_section ] . front ( ) ; E ; E = E . next ( ) ) {
r_keys - > push_back ( E . key ( ) ) ;
2014-02-10 01:10:30 +00:00
}
}
2017-03-05 15:44:50 +00:00
void ConfigFile : : erase_section ( const String & p_section ) {
2014-02-10 01:10:30 +00:00
2017-02-01 12:45:45 +00:00
values . erase ( p_section ) ;
}
2014-02-10 01:10:30 +00:00
2019-09-29 00:27:09 +00:00
void ConfigFile : : erase_section_key ( const String & p_section , const String & p_key ) {
ERR_FAIL_COND_MSG ( ! values . has ( p_section ) , " Cannot erase key from nonexistent section ' " + p_section + " '. " ) ;
values [ p_section ] . erase ( p_key ) ;
}
2017-03-05 15:44:50 +00:00
Error ConfigFile : : save ( const String & p_path ) {
2014-02-10 01:10:30 +00:00
Error err ;
2017-03-05 15:44:50 +00:00
FileAccess * file = FileAccess : : open ( p_path , FileAccess : : WRITE , & err ) ;
2014-02-10 01:10:30 +00:00
if ( err ) {
2016-01-10 12:59:14 +00:00
if ( file )
memdelete ( file ) ;
2014-02-10 01:10:30 +00:00
return err ;
}
2019-06-27 11:57:45 +00:00
return _internal_save ( file ) ;
}
Error ConfigFile : : save_encrypted ( const String & p_path , const Vector < uint8_t > & p_key ) {
Error err ;
FileAccess * f = FileAccess : : open ( p_path , FileAccess : : WRITE , & err ) ;
if ( err )
return err ;
FileAccessEncrypted * fae = memnew ( FileAccessEncrypted ) ;
err = fae - > open_and_parse ( f , p_key , FileAccessEncrypted : : MODE_WRITE_AES256 ) ;
if ( err ) {
memdelete ( fae ) ;
memdelete ( f ) ;
return err ;
}
return _internal_save ( fae ) ;
}
Error ConfigFile : : save_encrypted_pass ( const String & p_path , const String & p_pass ) {
Error err ;
FileAccess * f = FileAccess : : open ( p_path , FileAccess : : WRITE , & err ) ;
if ( err )
return err ;
FileAccessEncrypted * fae = memnew ( FileAccessEncrypted ) ;
err = fae - > open_and_parse_password ( f , p_pass , FileAccessEncrypted : : MODE_WRITE_AES256 ) ;
if ( err ) {
memdelete ( fae ) ;
memdelete ( f ) ;
return err ;
}
return _internal_save ( fae ) ;
}
Error ConfigFile : : _internal_save ( FileAccess * file ) {
2017-11-04 13:08:21 +00:00
for ( OrderedHashMap < String , OrderedHashMap < String , Variant > > : : Element E = values . front ( ) ; E ; E = E . next ( ) ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( E ! = values . front ( ) )
2014-02-10 01:10:30 +00:00
file - > store_string ( " \n " ) ;
2017-11-04 13:08:21 +00:00
file - > store_string ( " [ " + E . key ( ) + " ] \n \n " ) ;
2014-02-10 01:10:30 +00:00
2017-11-04 13:08:21 +00:00
for ( OrderedHashMap < String , Variant > : : Element F = E . get ( ) . front ( ) ; F ; F = F . next ( ) ) {
2014-02-10 01:10:30 +00:00
2015-12-31 03:31:00 +00:00
String vstr ;
2017-11-03 13:33:19 +00:00
VariantWriter : : write_to_string ( F . get ( ) , vstr ) ;
file - > store_string ( F . key ( ) + " = " + vstr + " \n " ) ;
2014-02-10 01:10:30 +00:00
}
}
memdelete ( file ) ;
return OK ;
}
2017-03-05 15:44:50 +00:00
Error ConfigFile : : load ( const String & p_path ) {
2014-02-10 01:10:30 +00:00
Error err ;
2017-03-05 15:44:50 +00:00
FileAccess * f = FileAccess : : open ( p_path , FileAccess : : READ , & err ) ;
2014-02-10 01:10:30 +00:00
2015-12-31 03:31:00 +00:00
if ( ! f )
2019-08-20 11:59:46 +00:00
return err ;
2014-02-10 01:10:30 +00:00
2019-06-27 11:57:45 +00:00
return _internal_load ( p_path , f ) ;
}
Error ConfigFile : : load_encrypted ( const String & p_path , const Vector < uint8_t > & p_key ) {
Error err ;
FileAccess * f = FileAccess : : open ( p_path , FileAccess : : READ , & err ) ;
if ( err )
return err ;
FileAccessEncrypted * fae = memnew ( FileAccessEncrypted ) ;
err = fae - > open_and_parse ( f , p_key , FileAccessEncrypted : : MODE_READ ) ;
if ( err ) {
memdelete ( fae ) ;
memdelete ( f ) ;
return err ;
}
return _internal_load ( p_path , fae ) ;
}
Error ConfigFile : : load_encrypted_pass ( const String & p_path , const String & p_pass ) {
Error err ;
FileAccess * f = FileAccess : : open ( p_path , FileAccess : : READ , & err ) ;
if ( err )
return err ;
FileAccessEncrypted * fae = memnew ( FileAccessEncrypted ) ;
err = fae - > open_and_parse_password ( f , p_pass , FileAccessEncrypted : : MODE_READ ) ;
if ( err ) {
memdelete ( fae ) ;
memdelete ( f ) ;
return err ;
}
return _internal_load ( p_path , fae ) ;
}
Error ConfigFile : : _internal_load ( const String & p_path , FileAccess * f ) {
2015-12-31 03:31:00 +00:00
VariantParser : : StreamFile stream ;
2017-03-05 15:44:50 +00:00
stream . f = f ;
2014-02-10 01:10:30 +00:00
2020-02-10 08:19:00 +00:00
Error err = _parse ( p_path , & stream ) ;
memdelete ( f ) ;
return err ;
}
Error ConfigFile : : parse ( const String & p_data ) {
VariantParser : : StreamString stream ;
stream . s = p_data ;
return _parse ( " <string> " , & stream ) ;
}
Error ConfigFile : : _parse ( const String & p_path , VariantParser : : Stream * p_stream ) {
2015-12-31 03:31:00 +00:00
String assign ;
Variant value ;
VariantParser : : Tag next_tag ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
int lines = 0 ;
2015-12-31 03:31:00 +00:00
String error_text ;
2014-02-10 01:10:30 +00:00
2015-12-31 03:31:00 +00:00
String section ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
while ( true ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
assign = Variant ( ) ;
2016-01-07 12:04:44 +00:00
next_tag . fields . clear ( ) ;
2017-03-05 15:44:50 +00:00
next_tag . name = String ( ) ;
2016-01-07 12:04:44 +00:00
2020-02-10 08:19:00 +00:00
Error err = VariantParser : : parse_tag_assign_eof ( p_stream , lines , error_text , next_tag , assign , value , NULL , true ) ;
2017-03-05 15:44:50 +00:00
if ( err = = ERR_FILE_EOF ) {
2015-12-31 03:31:00 +00:00
return OK ;
2017-03-05 15:44:50 +00:00
} else if ( err ! = OK ) {
2020-02-10 08:19:00 +00:00
ERR_PRINT ( " ConfgFile - " + p_path + " : " + itos ( lines ) + " error: " + error_text + " . " ) ;
2015-12-31 03:31:00 +00:00
return err ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
if ( assign ! = String ( ) ) {
set_value ( section , assign , value ) ;
} else if ( next_tag . name ! = String ( ) ) {
section = next_tag . name ;
2015-12-31 03:31:00 +00:00
}
2014-02-10 01:10:30 +00:00
}
2020-02-10 08:19:00 +00:00
return OK ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void ConfigFile : : _bind_methods ( ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_value " , " section " , " key " , " value " ) , & ConfigFile : : set_value ) ;
2017-08-09 11:19:41 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_value " , " section " , " key " , " default " ) , & ConfigFile : : get_value , DEFVAL ( Variant ( ) ) ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " has_section " , " section " ) , & ConfigFile : : has_section ) ;
ClassDB : : bind_method ( D_METHOD ( " has_section_key " , " section " , " key " ) , & ConfigFile : : has_section_key ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_sections " ) , & ConfigFile : : _get_sections ) ;
ClassDB : : bind_method ( D_METHOD ( " get_section_keys " , " section " ) , & ConfigFile : : _get_section_keys ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " erase_section " , " section " ) , & ConfigFile : : erase_section ) ;
2019-09-29 00:27:09 +00:00
ClassDB : : bind_method ( D_METHOD ( " erase_section_key " , " section " , " key " ) , & ConfigFile : : erase_section_key ) ;
2014-02-10 01:10:30 +00:00
2017-08-09 11:19:41 +00:00
ClassDB : : bind_method ( D_METHOD ( " load " , " path " ) , & ConfigFile : : load ) ;
2020-02-10 08:19:00 +00:00
ClassDB : : bind_method ( D_METHOD ( " parse " , " data " ) , & ConfigFile : : parse ) ;
2017-08-09 11:19:41 +00:00
ClassDB : : bind_method ( D_METHOD ( " save " , " path " ) , & ConfigFile : : save ) ;
2019-06-27 11:57:45 +00:00
ClassDB : : bind_method ( D_METHOD ( " load_encrypted " , " path " , " key " ) , & ConfigFile : : load_encrypted ) ;
ClassDB : : bind_method ( D_METHOD ( " load_encrypted_pass " , " path " , " pass " ) , & ConfigFile : : load_encrypted_pass ) ;
ClassDB : : bind_method ( D_METHOD ( " save_encrypted " , " path " , " key " ) , & ConfigFile : : save_encrypted ) ;
ClassDB : : bind_method ( D_METHOD ( " save_encrypted_pass " , " path " , " pass " ) , & ConfigFile : : save_encrypted_pass ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
ConfigFile : : ConfigFile ( ) {
2014-02-10 01:10:30 +00:00
}