2014-02-10 01:10:30 +00:00
/*************************************************************************/
/* file_access_pack.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
/*************************************************************************/
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) */
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 "file_access_pack.h"
2018-09-11 16:13:45 +00:00
# include "core/version.h"
2014-02-10 01:10:30 +00:00
# include <stdio.h>
2017-02-21 03:05:15 +00:00
# define PACK_VERSION 1
2014-02-15 05:01:39 +00:00
2019-09-23 20:41:25 +00:00
Error PackedData : : add_pack ( const String & p_path , bool p_replace_files ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < sources . size ( ) ; i + + ) {
2014-02-10 01:10:30 +00:00
2019-09-23 20:41:25 +00:00
if ( sources [ i ] - > try_open_pack ( p_path , p_replace_files ) ) {
2014-02-10 01:10:30 +00:00
return OK ;
} ;
} ;
return ERR_FILE_UNRECOGNIZED ;
} ;
2019-09-23 20:41:25 +00:00
void PackedData : : add_path ( const String & pkg_path , const String & path , uint64_t ofs , uint64_t size , const uint8_t * p_md5 , PackSource * p_src , bool p_replace_files ) {
2014-02-10 01:10:30 +00:00
2014-08-02 01:10:38 +00:00
PathMD5 pmd5 ( path . md5_buffer ( ) ) ;
//printf("adding path %ls, %lli, %lli\n", path.c_str(), pmd5.a, pmd5.b);
bool exists = files . has ( pmd5 ) ;
2014-02-10 01:10:30 +00:00
PackedFile pf ;
2017-03-05 15:44:50 +00:00
pf . pack = pkg_path ;
pf . offset = ofs ;
pf . size = size ;
for ( int i = 0 ; i < 16 ; i + + )
pf . md5 [ i ] = p_md5 [ i ] ;
2014-02-10 01:10:30 +00:00
pf . src = p_src ;
2019-09-23 20:41:25 +00:00
if ( ! exists | | p_replace_files )
files [ pmd5 ] = pf ;
2014-02-10 01:10:30 +00:00
if ( ! exists ) {
//search for dir
2017-03-05 15:44:50 +00:00
String p = path . replace_first ( " res:// " , " " ) ;
PackedDir * cd = root ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( p . find ( " / " ) ! = - 1 ) { //in a subdir
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
Vector < String > ds = p . get_base_dir ( ) . split ( " / " ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
for ( int j = 0 ; j < ds . size ( ) ; j + + ) {
2014-02-10 01:10:30 +00:00
if ( ! cd - > subdirs . has ( ds [ j ] ) ) {
2017-03-05 15:44:50 +00:00
PackedDir * pd = memnew ( PackedDir ) ;
pd - > name = ds [ j ] ;
pd - > parent = cd ;
cd - > subdirs [ pd - > name ] = pd ;
cd = pd ;
2014-02-10 01:10:30 +00:00
} else {
2017-03-05 15:44:50 +00:00
cd = cd - > subdirs [ ds [ j ] ] ;
2014-02-10 01:10:30 +00:00
}
}
}
2018-03-18 13:04:50 +00:00
String filename = path . get_file ( ) ;
2019-08-17 17:03:18 +00:00
// Don't add as a file if the path points to a directory
2018-03-18 13:04:50 +00:00
if ( ! filename . empty ( ) ) {
cd - > files . insert ( filename ) ;
}
2014-02-10 01:10:30 +00:00
}
}
void PackedData : : add_pack_source ( PackSource * p_source ) {
2015-05-06 23:37:25 +00:00
if ( p_source ! = NULL ) {
sources . push_back ( p_source ) ;
}
2014-02-10 01:10:30 +00:00
} ;
2017-03-05 15:44:50 +00:00
PackedData * PackedData : : singleton = NULL ;
2014-02-10 01:10:30 +00:00
PackedData : : PackedData ( ) {
2017-03-05 15:44:50 +00:00
singleton = this ;
root = memnew ( PackedDir ) ;
root - > parent = NULL ;
disabled = false ;
2014-02-10 01:10:30 +00:00
add_pack_source ( memnew ( PackedSourcePCK ) ) ;
}
2015-04-20 22:38:02 +00:00
void PackedData : : _free_packed_dirs ( PackedDir * p_dir ) {
2017-03-05 15:44:50 +00:00
for ( Map < String , PackedDir * > : : Element * E = p_dir - > subdirs . front ( ) ; E ; E = E - > next ( ) )
2015-04-20 22:38:02 +00:00
_free_packed_dirs ( E - > get ( ) ) ;
memdelete ( p_dir ) ;
}
PackedData : : ~ PackedData ( ) {
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < sources . size ( ) ; i + + ) {
2015-04-20 22:38:02 +00:00
memdelete ( sources [ i ] ) ;
}
_free_packed_dirs ( root ) ;
}
2014-02-10 01:10:30 +00:00
//////////////////////////////////////////////////////////////////
2019-09-23 20:41:25 +00:00
bool PackedSourcePCK : : try_open_pack ( const String & p_path , bool p_replace_files ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
FileAccess * f = FileAccess : : open ( p_path , FileAccess : : READ ) ;
2014-02-10 01:10:30 +00:00
if ( ! f )
return false ;
2014-08-02 01:10:38 +00:00
//printf("try open %ls!\n", p_path.c_str());
2017-03-05 15:44:50 +00:00
uint32_t magic = f - > get_32 ( ) ;
2014-02-10 01:10:30 +00:00
2014-02-15 05:01:39 +00:00
if ( magic ! = 0x43504447 ) {
2019-03-25 19:56:33 +00:00
//maybe at the end.... self contained exe
2014-02-10 01:10:30 +00:00
f - > seek_end ( ) ;
2017-09-10 13:37:49 +00:00
f - > seek ( f - > get_position ( ) - 4 ) ;
2014-02-10 01:10:30 +00:00
magic = f - > get_32 ( ) ;
2014-02-15 05:01:39 +00:00
if ( magic ! = 0x43504447 ) {
2014-02-10 01:10:30 +00:00
memdelete ( f ) ;
return false ;
}
2017-09-10 13:37:49 +00:00
f - > seek ( f - > get_position ( ) - 12 ) ;
2014-02-10 01:10:30 +00:00
uint64_t ds = f - > get_64 ( ) ;
2017-09-10 13:37:49 +00:00
f - > seek ( f - > get_position ( ) - ds - 8 ) ;
2014-02-10 01:10:30 +00:00
magic = f - > get_32 ( ) ;
2014-02-15 05:01:39 +00:00
if ( magic ! = 0x43504447 ) {
2014-02-10 01:10:30 +00:00
memdelete ( f ) ;
return false ;
}
}
2014-02-15 05:01:39 +00:00
uint32_t version = f - > get_32 ( ) ;
2014-02-10 01:10:30 +00:00
uint32_t ver_major = f - > get_32 ( ) ;
uint32_t ver_minor = f - > get_32 ( ) ;
2018-10-03 14:13:34 +00:00
f - > get_32 ( ) ; // ver_rev
2014-02-10 01:10:30 +00:00
2019-08-15 02:57:49 +00:00
ERR_FAIL_COND_V_MSG ( version ! = PACK_VERSION , false , " Pack version unsupported: " + itos ( version ) + " . " ) ;
ERR_FAIL_COND_V_MSG ( ver_major > VERSION_MAJOR | | ( ver_major = = VERSION_MAJOR & & ver_minor > VERSION_MINOR ) , false , " Pack created with a newer version of the engine: " + itos ( ver_major ) + " . " + itos ( ver_minor ) + " . " ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < 16 ; i + + ) {
2014-02-10 01:10:30 +00:00
//reserved
f - > get_32 ( ) ;
}
int file_count = f - > get_32 ( ) ;
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < file_count ; i + + ) {
2014-02-10 01:10:30 +00:00
uint32_t sl = f - > get_32 ( ) ;
CharString cs ;
2017-03-05 15:44:50 +00:00
cs . resize ( sl + 1 ) ;
f - > get_buffer ( ( uint8_t * ) cs . ptr ( ) , sl ) ;
cs [ sl ] = 0 ;
2014-02-10 01:10:30 +00:00
String path ;
path . parse_utf8 ( cs . ptr ( ) ) ;
uint64_t ofs = f - > get_64 ( ) ;
uint64_t size = f - > get_64 ( ) ;
2014-02-13 21:03:28 +00:00
uint8_t md5 [ 16 ] ;
2017-03-05 15:44:50 +00:00
f - > get_buffer ( md5 , 16 ) ;
2019-09-23 20:41:25 +00:00
PackedData : : get_singleton ( ) - > add_path ( p_path , path , ofs , size , md5 , this , p_replace_files ) ;
2014-02-10 01:10:30 +00:00
} ;
return true ;
} ;
2017-03-05 15:44:50 +00:00
FileAccess * PackedSourcePCK : : get_file ( const String & p_path , PackedData : : PackedFile * p_file ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
return memnew ( FileAccessPack ( p_path , * p_file ) ) ;
2014-02-10 01:10:30 +00:00
} ;
//////////////////////////////////////////////////////////////////
2017-03-05 15:44:50 +00:00
Error FileAccessPack : : _open ( const String & p_path , int p_mode_flags ) {
2014-02-10 01:10:30 +00:00
ERR_FAIL_V ( ERR_UNAVAILABLE ) ;
return ERR_UNAVAILABLE ;
}
void FileAccessPack : : close ( ) {
f - > close ( ) ;
}
2017-03-05 15:44:50 +00:00
bool FileAccessPack : : is_open ( ) const {
2014-02-10 01:10:30 +00:00
return f - > is_open ( ) ;
}
2017-03-05 15:44:50 +00:00
void FileAccessPack : : seek ( size_t p_position ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( p_position > pf . size ) {
eof = true ;
2014-02-10 01:10:30 +00:00
} else {
2017-03-05 15:44:50 +00:00
eof = false ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
f - > seek ( pf . offset + p_position ) ;
pos = p_position ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void FileAccessPack : : seek_end ( int64_t p_position ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
seek ( pf . size + p_position ) ;
2014-02-10 01:10:30 +00:00
}
2017-09-10 13:37:49 +00:00
size_t FileAccessPack : : get_position ( ) const {
2014-02-10 01:10:30 +00:00
return pos ;
}
2017-03-05 15:44:50 +00:00
size_t FileAccessPack : : get_len ( ) const {
2014-02-10 01:10:30 +00:00
return pf . size ;
}
2017-03-05 15:44:50 +00:00
bool FileAccessPack : : eof_reached ( ) const {
2014-02-10 01:10:30 +00:00
return eof ;
}
uint8_t FileAccessPack : : get_8 ( ) const {
2017-03-05 15:44:50 +00:00
if ( pos > = pf . size ) {
eof = true ;
2014-02-10 01:10:30 +00:00
return 0 ;
}
pos + + ;
return f - > get_8 ( ) ;
}
2017-03-05 15:44:50 +00:00
int FileAccessPack : : get_buffer ( uint8_t * p_dst , int p_length ) const {
2014-02-10 01:10:30 +00:00
if ( eof )
return 0 ;
2019-02-27 09:07:30 +00:00
uint64_t to_read = p_length ;
2017-03-05 15:44:50 +00:00
if ( to_read + pos > pf . size ) {
eof = true ;
to_read = int64_t ( pf . size ) - int64_t ( pos ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
pos + = p_length ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( to_read < = 0 )
2014-02-10 01:10:30 +00:00
return 0 ;
2017-03-05 15:44:50 +00:00
f - > get_buffer ( p_dst , to_read ) ;
2014-02-10 01:10:30 +00:00
return to_read ;
}
void FileAccessPack : : set_endian_swap ( bool p_swap ) {
FileAccess : : set_endian_swap ( p_swap ) ;
f - > set_endian_swap ( p_swap ) ;
}
Error FileAccessPack : : get_error ( ) const {
if ( eof )
return ERR_FILE_EOF ;
return OK ;
}
2017-09-22 05:56:02 +00:00
void FileAccessPack : : flush ( ) {
ERR_FAIL ( ) ;
}
2014-02-10 01:10:30 +00:00
void FileAccessPack : : store_8 ( uint8_t p_dest ) {
ERR_FAIL ( ) ;
}
2017-03-05 15:44:50 +00:00
void FileAccessPack : : store_buffer ( const uint8_t * p_src , int p_length ) {
2014-02-10 01:10:30 +00:00
ERR_FAIL ( ) ;
}
2017-03-05 15:44:50 +00:00
bool FileAccessPack : : file_exists ( const String & p_name ) {
2014-02-10 01:10:30 +00:00
return false ;
}
2017-12-06 20:36:34 +00:00
FileAccessPack : : FileAccessPack ( const String & p_path , const PackedData : : PackedFile & p_file ) :
pf ( p_file ) ,
f ( FileAccess : : open ( pf . pack , FileAccess : : READ ) ) {
2019-08-15 02:57:49 +00:00
ERR_FAIL_COND_MSG ( ! f , " Can't open pack-referenced file: " + String ( pf . pack ) + " . " ) ;
2014-02-10 01:10:30 +00:00
f - > seek ( pf . offset ) ;
2017-03-05 15:44:50 +00:00
pos = 0 ;
eof = false ;
2014-02-10 01:10:30 +00:00
}
FileAccessPack : : ~ FileAccessPack ( ) {
if ( f )
memdelete ( f ) ;
}
//////////////////////////////////////////////////////////////////////////////////
// DIR ACCESS
//////////////////////////////////////////////////////////////////////////////////
2017-01-14 12:16:41 +00:00
Error DirAccessPack : : list_dir_begin ( ) {
2014-02-10 01:10:30 +00:00
list_dirs . clear ( ) ;
list_files . clear ( ) ;
2017-03-05 15:44:50 +00:00
for ( Map < String , PackedData : : PackedDir * > : : Element * E = current - > subdirs . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 01:10:30 +00:00
list_dirs . push_back ( E - > key ( ) ) ;
}
2017-03-05 15:44:50 +00:00
for ( Set < String > : : Element * E = current - > files . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 01:10:30 +00:00
list_files . push_back ( E - > get ( ) ) ;
}
2017-01-14 12:16:41 +00:00
return OK ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
String DirAccessPack : : get_next ( ) {
2014-02-10 01:10:30 +00:00
if ( list_dirs . size ( ) ) {
2017-03-05 15:44:50 +00:00
cdir = true ;
2014-02-10 01:10:30 +00:00
String d = list_dirs . front ( ) - > get ( ) ;
list_dirs . pop_front ( ) ;
return d ;
} else if ( list_files . size ( ) ) {
2017-03-05 15:44:50 +00:00
cdir = false ;
2014-02-10 01:10:30 +00:00
String f = list_files . front ( ) - > get ( ) ;
list_files . pop_front ( ) ;
return f ;
} else {
return String ( ) ;
}
}
2017-03-05 15:44:50 +00:00
bool DirAccessPack : : current_is_dir ( ) const {
2014-02-10 01:10:30 +00:00
return cdir ;
}
2017-03-05 15:44:50 +00:00
bool DirAccessPack : : current_is_hidden ( ) const {
2015-03-21 17:33:32 +00:00
return false ;
}
2014-02-10 01:10:30 +00:00
void DirAccessPack : : list_dir_end ( ) {
list_dirs . clear ( ) ;
list_files . clear ( ) ;
}
int DirAccessPack : : get_drive_count ( ) {
return 0 ;
}
String DirAccessPack : : get_drive ( int p_drive ) {
return " " ;
}
Error DirAccessPack : : change_dir ( String p_dir ) {
2017-03-05 15:44:50 +00:00
String nd = p_dir . replace ( " \\ " , " / " ) ;
bool absolute = false ;
2014-02-10 01:10:30 +00:00
if ( nd . begins_with ( " res:// " ) ) {
2017-03-05 15:44:50 +00:00
nd = nd . replace_first ( " res:// " , " " ) ;
absolute = true ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
nd = nd . simplify_path ( ) ;
2014-02-10 01:10:30 +00:00
2016-09-03 22:35:42 +00:00
if ( nd = = " " ) nd = " . " ;
2014-02-10 01:10:30 +00:00
if ( nd . begins_with ( " / " ) ) {
2017-03-05 15:44:50 +00:00
nd = nd . replace_first ( " / " , " " ) ;
absolute = true ;
2014-02-10 01:10:30 +00:00
}
Vector < String > paths = nd . split ( " / " ) ;
PackedData : : PackedDir * pd ;
if ( absolute )
pd = PackedData : : get_singleton ( ) - > root ;
else
pd = current ;
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < paths . size ( ) ; i + + ) {
2014-02-10 01:10:30 +00:00
String p = paths [ i ] ;
2017-03-05 15:44:50 +00:00
if ( p = = " . " ) {
2014-02-10 01:10:30 +00:00
continue ;
2017-03-05 15:44:50 +00:00
} else if ( p = = " .. " ) {
2014-02-10 01:10:30 +00:00
if ( pd - > parent ) {
2017-03-05 15:44:50 +00:00
pd = pd - > parent ;
2014-02-10 01:10:30 +00:00
}
} else if ( pd - > subdirs . has ( p ) ) {
2017-03-05 15:44:50 +00:00
pd = pd - > subdirs [ p ] ;
2014-02-10 01:10:30 +00:00
} else {
return ERR_INVALID_PARAMETER ;
}
}
2017-03-05 15:44:50 +00:00
current = pd ;
2014-02-10 01:10:30 +00:00
return OK ;
}
String DirAccessPack : : get_current_dir ( ) {
PackedData : : PackedDir * pd = current ;
2017-05-04 00:20:53 +00:00
String p = current - > name ;
2014-02-10 01:10:30 +00:00
2017-05-04 00:20:53 +00:00
while ( pd - > parent ) {
pd = pd - > parent ;
2018-11-08 22:05:12 +00:00
p = pd - > name . plus_file ( p ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
return " res:// " + p ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
bool DirAccessPack : : file_exists ( String p_file ) {
2014-02-10 01:10:30 +00:00
2019-08-17 17:03:18 +00:00
p_file = fix_path ( p_file ) ;
2014-02-10 01:10:30 +00:00
return current - > files . has ( p_file ) ;
}
2014-05-25 03:34:51 +00:00
bool DirAccessPack : : dir_exists ( String p_dir ) {
2019-08-17 17:03:18 +00:00
p_dir = fix_path ( p_dir ) ;
2014-05-25 03:34:51 +00:00
return current - > subdirs . has ( p_dir ) ;
}
2017-03-05 15:44:50 +00:00
Error DirAccessPack : : make_dir ( String p_dir ) {
2014-02-10 01:10:30 +00:00
return ERR_UNAVAILABLE ;
}
2017-03-05 15:44:50 +00:00
Error DirAccessPack : : rename ( String p_from , String p_to ) {
2014-02-10 01:10:30 +00:00
return ERR_UNAVAILABLE ;
}
2017-03-05 15:44:50 +00:00
Error DirAccessPack : : remove ( String p_name ) {
2014-02-10 01:10:30 +00:00
return ERR_UNAVAILABLE ;
}
2017-03-05 15:44:50 +00:00
size_t DirAccessPack : : get_space_left ( ) {
2014-02-10 01:10:30 +00:00
return 0 ;
}
2019-01-21 18:23:08 +00:00
String DirAccessPack : : get_filesystem_type ( ) const {
return " PCK " ;
}
2014-02-10 01:10:30 +00:00
DirAccessPack : : DirAccessPack ( ) {
2017-03-05 15:44:50 +00:00
current = PackedData : : get_singleton ( ) - > root ;
cdir = false ;
2014-02-10 01:10:30 +00:00
}
DirAccessPack : : ~ DirAccessPack ( ) {
}