2014-02-10 02:10:30 +01:00
/*************************************************************************/
/* file_access_pack.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2014-02-10 02:10:30 +01:00
/*************************************************************************/
2022-01-03 21:27:34 +01:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2014-02-10 02:10:30 +01: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-05 00:50:27 +01:00
2014-02-10 02:10:30 +01:00
# ifndef FILE_ACCESS_PACK_H
# define FILE_ACCESS_PACK_H
2021-06-11 14:51:48 +02:00
# include "core/io/dir_access.h"
# include "core/io/file_access.h"
2020-11-07 23:33:38 +01:00
# include "core/string/print_string.h"
2022-05-19 17:00:06 +02:00
# include "core/templates/hash_set.h"
2020-11-07 23:33:38 +01:00
# include "core/templates/list.h"
2022-05-13 15:04:37 +02:00
# include "core/templates/rb_map.h"
2014-02-10 02:10:30 +01:00
2019-11-30 17:22:22 +01:00
// Godot's packed file magic header ("GDPC" in ASCII).
# define PACK_HEADER_MAGIC 0x43504447
// The current packed file format version number.
2020-04-28 19:51:29 +02:00
# define PACK_FORMAT_VERSION 2
enum PackFlags {
PACK_DIR_ENCRYPTED = 1 < < 0
} ;
enum PackFileFlags {
PACK_FILE_ENCRYPTED = 1 < < 0
} ;
2019-11-30 17:22:22 +01:00
2014-02-10 02:10:30 +01:00
class PackSource ;
class PackedData {
2017-03-05 16:44:50 +01:00
friend class FileAccessPack ;
friend class DirAccessPack ;
friend class PackSource ;
2014-02-10 02:10:30 +01:00
public :
struct PackedFile {
String pack ;
2014-02-13 22:03:28 +01:00
uint64_t offset ; //if offset is ZERO, the file was ERASED
2014-02-10 02:10:30 +01:00
uint64_t size ;
2014-02-13 22:03:28 +01:00
uint8_t md5 [ 16 ] ;
2022-04-04 15:06:57 +02:00
PackSource * src = nullptr ;
2020-04-28 19:51:29 +02:00
bool encrypted ;
2014-02-10 02:10:30 +01:00
} ;
private :
struct PackedDir {
2020-05-12 17:01:17 +02:00
PackedDir * parent = nullptr ;
2014-02-10 02:10:30 +01:00
String name ;
2022-05-13 15:04:37 +02:00
HashMap < String , PackedDir * > subdirs ;
2022-05-19 17:00:06 +02:00
HashSet < String > files ;
2014-02-10 02:10:30 +01:00
} ;
2014-08-02 03:10:38 +02:00
struct PathMD5 {
2020-05-12 17:01:17 +02:00
uint64_t a = 0 ;
uint64_t b = 0 ;
2014-08-02 03:10:38 +02:00
2022-05-13 15:04:37 +02:00
bool operator = = ( const PathMD5 & p_val ) const {
return ( a = = p_val . a ) & & ( b = = p_val . b ) ;
}
static uint32_t hash ( const PathMD5 & p_val ) {
2022-06-18 16:20:55 +02:00
uint32_t h = hash_murmur3_one_32 ( p_val . a ) ;
return hash_fmix32 ( hash_murmur3_one_32 ( p_val . b , h ) ) ;
2020-05-19 15:46:49 +02:00
}
2014-08-02 03:10:38 +02:00
2020-05-12 17:01:17 +02:00
PathMD5 ( ) { }
2014-08-02 03:10:38 +02:00
2022-04-07 12:23:40 +02:00
explicit PathMD5 ( const Vector < uint8_t > & p_buf ) {
2017-03-05 16:44:50 +01:00
a = * ( ( uint64_t * ) & p_buf [ 0 ] ) ;
b = * ( ( uint64_t * ) & p_buf [ 8 ] ) ;
2020-05-12 17:01:17 +02:00
}
2014-08-02 03:10:38 +02:00
} ;
2022-05-13 15:04:37 +02:00
HashMap < PathMD5 , PackedFile , PathMD5 > files ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
Vector < PackSource * > sources ;
2014-02-10 02:10:30 +01:00
2022-04-04 15:06:57 +02:00
PackedDir * root = nullptr ;
2014-02-10 02:10:30 +01:00
static PackedData * singleton ;
2020-05-12 17:01:17 +02:00
bool disabled = false ;
2014-02-10 02:10:30 +01:00
2015-04-21 00:38:02 +02:00
void _free_packed_dirs ( PackedDir * p_dir ) ;
2014-02-10 02:10:30 +01:00
public :
2017-03-05 16:44:50 +01:00
void add_pack_source ( PackSource * p_source ) ;
2019-03-26 18:51:13 +01:00
void add_path ( const String & p_pkg_path , const String & p_path , uint64_t p_ofs , uint64_t p_size , const uint8_t * p_md5 , PackSource * p_src , bool p_replace_files , bool p_encrypted = false ) ; // for PackSource
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
void set_disabled ( bool p_disabled ) { disabled = p_disabled ; }
2014-02-10 02:10:30 +01:00
_FORCE_INLINE_ bool is_disabled ( ) const { return disabled ; }
static PackedData * get_singleton ( ) { return singleton ; }
2019-03-26 18:51:13 +01:00
Error add_pack ( const String & p_path , bool p_replace_files , uint64_t p_offset ) ;
2014-02-10 02:10:30 +01:00
2022-03-23 10:08:58 +01:00
_FORCE_INLINE_ Ref < FileAccess > try_open_path ( const String & p_path ) ;
2017-03-05 16:44:50 +01:00
_FORCE_INLINE_ bool has_path ( const String & p_path ) ;
2014-02-10 02:10:30 +01:00
2022-03-23 10:08:58 +01:00
_FORCE_INLINE_ Ref < DirAccess > try_open_directory ( const String & p_path ) ;
2020-07-27 12:06:40 +02:00
_FORCE_INLINE_ bool has_directory ( const String & p_path ) ;
2014-02-10 02:10:30 +01:00
PackedData ( ) ;
2015-04-21 00:38:02 +02:00
~ PackedData ( ) ;
2014-02-10 02:10:30 +01:00
} ;
class PackSource {
public :
2019-03-26 18:51:13 +01:00
virtual bool try_open_pack ( const String & p_path , bool p_replace_files , uint64_t p_offset ) = 0 ;
2022-03-23 10:08:58 +01:00
virtual Ref < FileAccess > get_file ( const String & p_path , PackedData : : PackedFile * p_file ) = 0 ;
2015-04-21 00:38:02 +02:00
virtual ~ PackSource ( ) { }
2014-02-10 02:10:30 +01:00
} ;
class PackedSourcePCK : public PackSource {
public :
2022-04-05 12:40:26 +02:00
virtual bool try_open_pack ( const String & p_path , bool p_replace_files , uint64_t p_offset ) override ;
2022-03-23 10:08:58 +01:00
virtual Ref < FileAccess > get_file ( const String & p_path , PackedData : : PackedFile * p_file ) override ;
2014-02-10 02:10:30 +01:00
} ;
class FileAccessPack : public FileAccess {
PackedData : : PackedFile pf ;
2019-03-26 18:51:13 +01:00
mutable uint64_t pos ;
2014-02-10 02:10:30 +01:00
mutable bool eof ;
2020-04-28 19:51:29 +02:00
uint64_t off ;
2014-02-10 02:10:30 +01:00
2022-03-23 10:08:58 +01:00
Ref < FileAccess > f ;
2017-03-05 16:44:50 +01:00
virtual Error _open ( const String & p_path , int p_mode_flags ) ;
virtual uint64_t _get_modified_time ( const String & p_file ) { return 0 ; }
2019-04-07 20:46:52 +02:00
virtual uint32_t _get_unix_permissions ( const String & p_file ) { return 0 ; }
virtual Error _set_unix_permissions ( const String & p_file , uint32_t p_permissions ) { return FAILED ; }
2014-02-10 02:10:30 +01:00
public :
virtual bool is_open ( ) const ;
2019-03-26 18:51:13 +01:00
virtual void seek ( uint64_t p_position ) ;
2017-03-05 16:44:50 +01:00
virtual void seek_end ( int64_t p_position = 0 ) ;
2019-03-26 18:51:13 +01:00
virtual uint64_t get_position ( ) const ;
2021-05-25 08:58:49 +02:00
virtual uint64_t get_length ( ) const ;
2014-02-10 02:10:30 +01:00
virtual bool eof_reached ( ) const ;
virtual uint8_t get_8 ( ) const ;
2019-03-26 18:51:13 +01:00
virtual uint64_t get_buffer ( uint8_t * p_dst , uint64_t p_length ) const ;
2014-02-10 02:10:30 +01:00
2021-05-20 14:58:03 +02:00
virtual void set_big_endian ( bool p_big_endian ) ;
2014-02-10 02:10:30 +01:00
virtual Error get_error ( ) const ;
2017-09-22 07:56:02 +02:00
virtual void flush ( ) ;
2014-02-10 02:10:30 +01:00
virtual void store_8 ( uint8_t p_dest ) ;
2019-03-26 18:51:13 +01:00
virtual void store_buffer ( const uint8_t * p_src , uint64_t p_length ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
virtual bool file_exists ( const String & p_name ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
FileAccessPack ( const String & p_path , const PackedData : : PackedFile & p_file ) ;
2014-02-10 02:10:30 +01:00
} ;
2022-03-23 10:08:58 +01:00
Ref < FileAccess > PackedData : : try_open_path ( const String & p_path ) {
2014-08-02 03:10:38 +02:00
PathMD5 pmd5 ( p_path . md5_buffer ( ) ) ;
2022-05-13 15:04:37 +02:00
HashMap < PathMD5 , PackedFile , PathMD5 > : : Iterator E = files . find ( pmd5 ) ;
2020-05-14 16:41:43 +02:00
if ( ! E ) {
2020-04-02 01:20:12 +02:00
return nullptr ; //not found
2020-05-14 16:41:43 +02:00
}
2022-05-13 15:04:37 +02:00
if ( E - > value . offset = = 0 ) {
2020-04-02 01:20:12 +02:00
return nullptr ; //was erased
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2022-05-13 15:04:37 +02:00
return E - > value . src - > get_file ( p_path , & E - > value ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
bool PackedData : : has_path ( const String & p_path ) {
2014-08-02 03:10:38 +02:00
return files . has ( PathMD5 ( p_path . md5_buffer ( ) ) ) ;
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:06:40 +02:00
bool PackedData : : has_directory ( const String & p_path ) {
2022-03-23 10:08:58 +01:00
Ref < DirAccess > da = try_open_directory ( p_path ) ;
if ( da . is_valid ( ) ) {
2020-07-27 12:06:40 +02:00
return true ;
} else {
return false ;
}
}
2014-02-10 02:10:30 +01:00
class DirAccessPack : public DirAccess {
PackedData : : PackedDir * current ;
List < String > list_dirs ;
List < String > list_files ;
2020-05-12 17:01:17 +02:00
bool cdir = false ;
2014-02-10 02:10:30 +01:00
2020-07-27 12:06:40 +02:00
PackedData : : PackedDir * _find_dir ( String p_dir ) ;
2014-02-10 02:10:30 +01:00
public :
2017-01-14 13:16:41 +01:00
virtual Error list_dir_begin ( ) ;
2014-02-10 02:10:30 +01:00
virtual String get_next ( ) ;
virtual bool current_is_dir ( ) const ;
2015-03-21 18:33:32 +01:00
virtual bool current_is_hidden ( ) const ;
2014-02-10 02:10:30 +01:00
virtual void list_dir_end ( ) ;
virtual int get_drive_count ( ) ;
virtual String get_drive ( int p_drive ) ;
virtual Error change_dir ( String p_dir ) ;
2022-03-23 10:08:58 +01:00
virtual String get_current_dir ( bool p_include_drive = true ) const ;
2014-02-10 02:10:30 +01:00
virtual bool file_exists ( String p_file ) ;
2014-05-25 05:34:51 +02:00
virtual bool dir_exists ( String p_dir ) ;
2014-02-10 02:10:30 +01:00
virtual Error make_dir ( String p_dir ) ;
virtual Error rename ( String p_from , String p_to ) ;
virtual Error remove ( String p_name ) ;
2019-03-26 18:51:13 +01:00
uint64_t get_space_left ( ) ;
2014-02-10 02:10:30 +01:00
2021-03-10 11:55:31 +01:00
virtual bool is_link ( String p_file ) { return false ; }
virtual String read_link ( String p_file ) { return p_file ; }
virtual Error create_link ( String p_source , String p_target ) { return FAILED ; }
2019-01-21 19:23:08 +01:00
virtual String get_filesystem_type ( ) const ;
2014-02-10 02:10:30 +01:00
DirAccessPack ( ) ;
} ;
2022-03-23 10:08:58 +01:00
Ref < DirAccess > PackedData : : try_open_directory ( const String & p_path ) {
Ref < DirAccess > da = memnew ( DirAccessPack ( ) ) ;
2020-07-27 12:06:40 +02:00
if ( da - > change_dir ( p_path ) ! = OK ) {
2022-03-23 10:08:58 +01:00
da = Ref < DirAccess > ( ) ;
2020-07-27 12:06:40 +02:00
}
return da ;
}
2014-02-10 02:10:30 +01:00
# endif // FILE_ACCESS_PACK_H