2012-04-28 22:07:47 -03:00
/*
2022-05-18 21:05:59 -03:00
* Copyright ( c ) 2010 - 2022 OTClient < https : //github.com/edubart/otclient>
2012-04-28 22:07:47 -03: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 .
*/
2021-11-25 21:34:36 -03:00
# include <filesystem>
2024-09-14 18:33:15 -03:00
# include <client/game.h>
2012-04-28 22:07:47 -03:00
# include "resourcemanager.h"
# include "filestream.h"
2012-07-13 22:10:24 -03:00
# include <framework/core/application.h>
# include <framework/luaengine/luainterface.h>
2013-02-22 19:29:58 -03:00
# include <framework/platform/platform.h>
2023-02-16 19:19:26 +01:00
# include <framework/net/protocolhttp.h>
2023-05-11 11:38:24 -03:00
# include <framework/util/crypt.h>
2024-05-28 20:05:13 -03:00
# include <framework/graphics/drawpoolmanager.h>
2012-04-28 22:07:47 -03:00
# include <physfs.h>
ResourceManager g_resources ;
2020-07-02 13:00:13 -03:00
void ResourceManager : : init ( const char * argv0 )
2012-04-28 22:07:47 -03:00
{
PHYSFS_init ( argv0 ) ;
2012-10-24 18:03:15 -02:00
PHYSFS_permitSymbolicLinks ( 1 ) ;
2023-05-11 11:38:24 -03:00
# if defined(WIN32)
char fileName [ 255 ] ;
GetModuleFileNameA ( NULL , fileName , sizeof ( fileName ) ) ;
m_binaryPath = std : : filesystem : : absolute ( fileName ) ;
# elif defined(ANDROID)
// nothing
# else
m_binaryPath = std : : filesystem : : absolute ( argv0 ) ;
# endif
2012-04-28 22:07:47 -03:00
}
void ResourceManager : : terminate ( )
{
PHYSFS_deinit ( ) ;
}
2022-06-20 18:46:32 -03:00
bool ResourceManager : : discoverWorkDir ( const std : : string & existentFile )
2012-06-19 21:15:56 -03:00
{
// search for modules directory
2013-02-24 17:26:19 -03:00
std : : string possiblePaths [ ] = { g_platform . getCurrentDir ( ) ,
2013-01-08 19:31:41 -02:00
g_resources . getBaseDir ( ) ,
2022-09-23 15:01:16 -03:00
g_resources . getBaseDir ( ) + " /game_data/ " ,
2013-01-23 13:05:18 -02:00
g_resources . getBaseDir ( ) + " ../ " ,
2013-01-23 13:43:39 -02:00
g_resources . getBaseDir ( ) + " ../share/ " + g_app . getCompactName ( ) + " / " } ;
2013-02-24 17:26:19 -03:00
2012-06-19 21:15:56 -03:00
bool found = false ;
2023-06-30 20:13:09 -03:00
for ( const auto & dir : possiblePaths ) {
2022-06-20 18:46:32 -03:00
if ( ! PHYSFS_mount ( dir . c_str ( ) , nullptr , 0 ) )
2012-10-24 18:03:15 -02:00
continue ;
2022-06-20 18:46:32 -03:00
if ( PHYSFS_exists ( existentFile . c_str ( ) ) ) {
2013-02-05 20:00:28 -02:00
g_logger . debug ( stdext : : format ( " Found work dir at '%s' " , dir ) ) ;
2012-06-19 21:15:56 -03:00
m_workDir = dir ;
found = true ;
break ;
}
2022-06-20 18:46:32 -03:00
PHYSFS_unmount ( dir . c_str ( ) ) ;
2012-06-19 21:15:56 -03:00
}
2013-01-08 19:31:41 -02:00
return found ;
2012-06-19 21:15:56 -03:00
}
2022-06-20 18:46:32 -03:00
bool ResourceManager : : setupUserWriteDir ( const std : : string & appWriteDirName )
2012-04-28 22:07:47 -03:00
{
2022-06-20 18:46:32 -03:00
const std : : string userDir = getUserDir ( ) ;
2012-06-19 21:15:56 -03:00
std : : string dirName ;
# ifndef WIN32
dirName = stdext : : format ( " .%s " , appWriteDirName ) ;
# else
dirName = appWriteDirName ;
# endif
2022-06-20 18:46:32 -03:00
const std : : string writeDir = userDir + dirName ;
2013-01-08 19:31:41 -02:00
2022-06-20 18:46:32 -03:00
if ( ! PHYSFS_setWriteDir ( writeDir . c_str ( ) ) ) {
if ( ! PHYSFS_setWriteDir ( userDir . c_str ( ) ) | | ! PHYSFS_mkdir ( dirName . c_str ( ) ) ) {
2019-10-10 03:14:48 +02:00
g_logger . error ( stdext : : format ( " Unable to create write directory '%s': %s " , writeDir , PHYSFS_getErrorByCode ( PHYSFS_getLastErrorCode ( ) ) ) ) ;
2012-10-24 18:03:15 -02:00
return false ;
}
}
2012-07-16 16:35:14 -03:00
return setWriteDir ( writeDir ) ;
}
2022-06-20 18:46:32 -03:00
bool ResourceManager : : setWriteDir ( const std : : string & writeDir , bool )
2012-07-16 16:35:14 -03:00
{
2022-06-20 18:46:32 -03:00
if ( ! PHYSFS_setWriteDir ( writeDir . c_str ( ) ) ) {
2019-10-10 03:14:48 +02:00
g_logger . error ( stdext : : format ( " Unable to set write directory '%s': %s " , writeDir , PHYSFS_getErrorByCode ( PHYSFS_getLastErrorCode ( ) ) ) ) ;
2012-07-16 16:35:14 -03:00
return false ;
2012-04-28 22:07:47 -03:00
}
2012-07-16 16:35:14 -03:00
2022-04-04 20:57:52 -03:00
if ( ! m_writeDir . empty ( ) )
2012-07-16 16:35:14 -03:00
removeSearchPath ( m_writeDir ) ;
m_writeDir = writeDir ;
2022-04-04 20:57:52 -03:00
if ( ! addSearchPath ( writeDir ) )
2012-07-17 21:31:55 +02:00
g_logger . error ( stdext : : format ( " Unable to add write '%s' directory to search path " , writeDir ) ) ;
2012-07-16 16:35:14 -03:00
2012-04-28 22:07:47 -03:00
return true ;
}
2022-06-20 18:46:32 -03:00
bool ResourceManager : : addSearchPath ( const std : : string & path , bool pushFront )
2012-04-28 22:07:47 -03:00
{
2022-06-20 18:46:32 -03:00
std : : string savePath = path ;
if ( ! PHYSFS_mount ( path . c_str ( ) , nullptr , pushFront ? 0 : 1 ) ) {
2012-08-23 04:17:19 -03:00
bool found = false ;
2023-06-30 20:13:09 -03:00
for ( const auto & searchPath : m_searchPaths ) {
2022-06-20 18:46:32 -03:00
std : : string newPath = searchPath + path ;
if ( PHYSFS_mount ( newPath . c_str ( ) , nullptr , pushFront ? 0 : 1 ) ) {
2012-08-23 04:17:19 -03:00
savePath = newPath ;
found = true ;
break ;
}
}
2022-04-04 20:57:52 -03:00
if ( ! found ) {
2019-10-10 03:14:48 +02:00
//g_logger.error(stdext::format("Could not add '%s' to directory search path. Reason %s", path, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())));
2012-08-23 04:17:19 -03:00
return false ;
}
}
2022-04-04 20:57:52 -03:00
if ( pushFront )
2012-08-23 04:17:19 -03:00
m_searchPaths . push_front ( savePath ) ;
2012-07-16 16:35:14 -03:00
else
2012-08-23 04:17:19 -03:00
m_searchPaths . push_back ( savePath ) ;
2012-04-28 22:07:47 -03:00
return true ;
}
2022-06-20 18:46:32 -03:00
bool ResourceManager : : removeSearchPath ( const std : : string & path )
2012-06-19 05:46:49 -03:00
{
2022-06-20 18:46:32 -03:00
if ( ! PHYSFS_unmount ( path . c_str ( ) ) )
2012-06-19 05:46:49 -03:00
return false ;
2021-12-03 13:59:39 -03:00
const auto it = std : : find ( m_searchPaths . begin ( ) , m_searchPaths . end ( ) , path ) ;
2012-07-16 16:35:14 -03:00
assert ( it ! = m_searchPaths . end ( ) ) ;
m_searchPaths . erase ( it ) ;
2012-06-19 05:46:49 -03:00
return true ;
}
2022-06-20 18:46:32 -03:00
void ResourceManager : : searchAndAddPackages ( const std : : string & packagesDir , const std : : string & packageExt )
2012-04-28 22:07:47 -03:00
{
2012-07-16 16:35:14 -03:00
auto files = listDirectoryFiles ( packagesDir ) ;
2022-09-23 15:01:16 -03:00
for ( auto it = files . rbegin ( ) ; it ! = files . rend ( ) ; + + it ) {
const auto & file = * it ;
2022-04-04 20:57:52 -03:00
if ( ! file . ends_with ( packageExt ) )
2012-07-16 16:35:14 -03:00
continue ;
std : : string package = getRealDir ( packagesDir ) + " / " + file ;
2022-04-04 20:57:52 -03:00
if ( ! addSearchPath ( package , true ) )
2019-10-10 03:14:48 +02:00
g_logger . error ( stdext : : format ( " Unable to read package '%s': %s " , package , PHYSFS_getErrorByCode ( PHYSFS_getLastErrorCode ( ) ) ) ) ;
2012-04-28 22:07:47 -03:00
}
}
2022-06-20 18:46:32 -03:00
bool ResourceManager : : fileExists ( const std : : string & fileName )
2012-04-28 22:07:47 -03:00
{
2023-02-16 19:19:26 +01:00
if ( fileName . find ( " /downloads " ) ! = std : : string : : npos )
return g_http . getFile ( fileName . substr ( 10 ) ) ! = nullptr ;
2022-06-20 18:46:32 -03:00
return ( PHYSFS_exists ( resolvePath ( fileName ) . c_str ( ) ) & & ! directoryExists ( fileName ) ) ;
2012-04-28 22:07:47 -03:00
}
2022-06-20 18:46:32 -03:00
bool ResourceManager : : directoryExists ( const std : : string & directoryName )
2012-04-28 22:07:47 -03:00
{
2023-02-16 19:19:26 +01:00
if ( directoryName = = " /downloads " )
return true ;
2019-10-11 03:23:01 +02:00
PHYSFS_Stat stat = { } ;
2022-06-20 18:46:32 -03:00
if ( ! PHYSFS_stat ( resolvePath ( directoryName ) . c_str ( ) , & stat ) ) {
2019-10-11 03:23:01 +02:00
return false ;
}
return stat . filetype = = PHYSFS_FILETYPE_DIRECTORY ;
2012-04-28 22:07:47 -03:00
}
2022-06-20 18:46:32 -03:00
void ResourceManager : : readFileStream ( const std : : string & fileName , std : : iostream & out )
2012-04-28 22:07:47 -03:00
{
2022-06-20 18:46:32 -03:00
const std : : string buffer = readFileContents ( fileName ) ;
2022-04-04 20:57:52 -03:00
if ( buffer . length ( ) = = 0 ) {
2013-01-08 19:31:41 -02:00
out . clear ( std : : ios : : eofbit ) ;
return ;
2012-04-28 22:07:47 -03:00
}
2013-01-08 19:31:41 -02:00
out . clear ( std : : ios : : goodbit ) ;
out . write ( & buffer [ 0 ] , buffer . length ( ) ) ;
out . seekg ( 0 , std : : ios : : beg ) ;
2012-04-28 22:07:47 -03:00
}
2022-06-20 18:46:32 -03:00
std : : string ResourceManager : : readFileContents ( const std : : string & fileName )
2012-04-28 22:07:47 -03:00
{
2022-06-20 18:46:32 -03:00
const std : : string fullPath = resolvePath ( fileName ) ;
2013-01-08 19:31:41 -02:00
2024-09-14 18:33:15 -03:00
if ( fullPath . find ( g_resources . getByteStrings ( 0 ) ) ! = std : : string : : npos ) {
2023-02-16 19:19:26 +01:00
auto dfile = g_http . getFile ( fullPath . substr ( 10 ) ) ;
if ( dfile )
return std : : string ( dfile - > response . begin ( ) , dfile - > response . end ( ) ) ;
}
2022-06-20 18:46:32 -03:00
PHYSFS_File * file = PHYSFS_openRead ( fullPath . c_str ( ) ) ;
2022-04-04 20:57:52 -03:00
if ( ! file )
2022-08-03 00:45:12 -03:00
throw Exception ( " unable to open file '%s': %s " , fullPath , PHYSFS_getErrorByCode ( PHYSFS_getLastErrorCode ( ) ) ) ;
2013-01-08 19:31:41 -02:00
2021-12-03 13:59:39 -03:00
const int fileSize = PHYSFS_fileLength ( file ) ;
2013-01-08 19:31:41 -02:00
std : : string buffer ( fileSize , 0 ) ;
2022-05-19 01:26:29 -03:00
PHYSFS_readBytes ( file , & buffer [ 0 ] , fileSize ) ;
2013-01-08 19:31:41 -02:00
PHYSFS_close ( file ) ;
2024-09-14 18:33:15 -03:00
bool hasHeader = false ;
if ( buffer . size ( ) > = std : : string ( ENCRYPTION_HEADER ) . size ( ) & &
buffer . substr ( 0 , std : : string ( ENCRYPTION_HEADER ) . size ( ) ) = = std : : string ( ENCRYPTION_HEADER ) ) {
hasHeader = true ;
}
2021-11-25 03:12:20 +01:00
# if ENABLE_ENCRYPTION == 1
2024-09-14 18:33:15 -03:00
if ( g_game . getFeature ( Otc : : GameAllowCustomBotScripts ) ) {
if ( fullPath . find ( g_resources . getByteStrings ( 1 ) ) ! = std : : string : : npos & & ! hasHeader ) {
return buffer ;
}
}
if ( hasHeader ) {
buffer = buffer . substr ( std : : string ( ENCRYPTION_HEADER ) . size ( ) ) ;
buffer = decrypt ( buffer ) ;
}
2021-11-25 03:12:20 +01:00
# endif
2013-01-08 19:31:41 -02:00
return buffer ;
2012-04-28 22:07:47 -03:00
}
2023-05-11 11:38:24 -03:00
bool ResourceManager : : writeFileBuffer ( const std : : string & fileName , const uint8_t * data , uint32_t size , bool createDirectory )
2012-04-28 22:07:47 -03:00
{
2023-05-11 11:38:24 -03:00
if ( createDirectory ) {
const auto & path = std : : filesystem : : path ( fileName ) ;
const auto & dirPath = path . parent_path ( ) . string ( ) ;
if ( ! PHYSFS_isDirectory ( dirPath . c_str ( ) ) ) {
if ( ! PHYSFS_mkdir ( dirPath . c_str ( ) ) ) {
g_logger . error ( stdext : : format ( " Unable to create write directory '%s': %s " , dirPath , PHYSFS_getErrorByCode ( PHYSFS_getLastErrorCode ( ) ) ) ) ;
return false ;
}
}
}
2022-06-20 18:46:32 -03:00
PHYSFS_file * file = PHYSFS_openWrite ( fileName . c_str ( ) ) ;
2022-04-04 20:57:52 -03:00
if ( ! file ) {
2019-10-10 03:14:48 +02:00
g_logger . error ( PHYSFS_getErrorByCode ( PHYSFS_getLastErrorCode ( ) ) ) ;
2012-04-28 22:07:47 -03:00
return false ;
}
2022-05-19 01:26:29 -03:00
PHYSFS_writeBytes ( file , data , size ) ;
2012-04-28 22:07:47 -03:00
PHYSFS_close ( file ) ;
return true ;
}
2022-06-20 18:46:32 -03:00
bool ResourceManager : : writeFileStream ( const std : : string & fileName , std : : iostream & in )
2012-04-28 22:07:47 -03:00
{
2021-12-03 13:59:39 -03:00
const std : : streampos oldPos = in . tellg ( ) ;
2012-04-28 22:07:47 -03:00
in . seekg ( 0 , std : : ios : : end ) ;
2021-12-03 13:59:39 -03:00
const std : : streampos size = in . tellg ( ) ;
2012-04-28 22:07:47 -03:00
in . seekg ( 0 , std : : ios : : beg ) ;
std : : vector < char > buffer ( size ) ;
in . read ( & buffer [ 0 ] , size ) ;
2022-07-04 19:46:30 -03:00
const bool ret = writeFileBuffer ( fileName , ( const uint8_t * ) & buffer [ 0 ] , size ) ;
2012-04-28 22:07:47 -03:00
in . seekg ( oldPos , std : : ios : : beg ) ;
return ret ;
}
2022-06-20 18:46:32 -03:00
bool ResourceManager : : writeFileContents ( const std : : string & fileName , const std : : string & data )
2012-04-28 22:07:47 -03:00
{
2021-11-25 03:12:20 +01:00
# if ENABLE_ENCRYPTION == 1
2024-09-14 18:33:15 -03:00
std : : string encryptedData = encrypt ( data , std : : string ( ENCRYPTION_PASSWORD ) ) ;
std : : string finalData = std : : string ( ENCRYPTION_HEADER ) + encryptedData ;
return writeFileBuffer ( fileName , ( const uint8_t * ) finalData . c_str ( ) , finalData . size ( ) ) ;
2021-11-25 03:12:20 +01:00
# else
2022-07-04 19:46:30 -03:00
return writeFileBuffer ( fileName , ( const uint8_t * ) data . c_str ( ) , data . size ( ) ) ;
2021-11-25 03:12:20 +01:00
# endif
2012-04-28 22:07:47 -03:00
}
2022-06-20 18:46:32 -03:00
FileStreamPtr ResourceManager : : openFile ( const std : : string & fileName )
2012-04-28 22:07:47 -03:00
{
2022-06-20 18:46:32 -03:00
const std : : string fullPath = resolvePath ( fileName ) ;
2013-01-08 19:31:41 -02:00
2022-06-20 18:46:32 -03:00
PHYSFS_File * file = PHYSFS_openRead ( fullPath . c_str ( ) ) ;
2022-04-04 20:57:52 -03:00
if ( ! file )
2022-08-03 00:45:12 -03:00
throw Exception ( " unable to open file '%s': %s " , fullPath , PHYSFS_getErrorByCode ( PHYSFS_getLastErrorCode ( ) ) ) ;
2023-01-04 22:52:30 -03:00
return { std : : make_shared < FileStream > ( fullPath , file , false ) } ;
2012-04-28 22:07:47 -03:00
}
2023-01-24 22:09:04 -03:00
FileStreamPtr ResourceManager : : appendFile ( const std : : string & fileName ) const
2012-04-28 22:07:47 -03:00
{
2022-06-20 18:46:32 -03:00
PHYSFS_File * file = PHYSFS_openAppend ( fileName . c_str ( ) ) ;
2022-04-04 20:57:52 -03:00
if ( ! file )
2022-08-03 00:45:12 -03:00
throw Exception ( " failed to append file '%s': %s " , fileName , PHYSFS_getErrorByCode ( PHYSFS_getLastErrorCode ( ) ) ) ;
2023-01-04 22:52:30 -03:00
return { std : : make_shared < FileStream > ( fileName , file , true ) } ;
2012-04-28 22:07:47 -03:00
}
2023-01-24 22:09:04 -03:00
FileStreamPtr ResourceManager : : createFile ( const std : : string & fileName ) const
2012-04-28 22:07:47 -03:00
{
2022-06-20 18:46:32 -03:00
PHYSFS_File * file = PHYSFS_openWrite ( fileName . c_str ( ) ) ;
2022-04-04 20:57:52 -03:00
if ( ! file )
2022-08-03 00:45:12 -03:00
throw Exception ( " failed to create file '%s': %s " , fileName , PHYSFS_getErrorByCode ( PHYSFS_getLastErrorCode ( ) ) ) ;
2023-01-04 22:52:30 -03:00
return { std : : make_shared < FileStream > ( fileName , file , true ) } ;
2012-04-28 22:07:47 -03:00
}
2022-06-20 18:46:32 -03:00
bool ResourceManager : : deleteFile ( const std : : string & fileName )
2012-04-28 22:07:47 -03:00
{
2022-06-20 18:46:32 -03:00
return PHYSFS_delete ( resolvePath ( fileName ) . c_str ( ) ) ! = 0 ;
2012-04-28 22:07:47 -03:00
}
2022-06-20 18:46:32 -03:00
bool ResourceManager : : makeDir ( const std : : string & directory )
2012-04-28 22:07:47 -03:00
{
2022-06-20 18:46:32 -03:00
return PHYSFS_mkdir ( directory . c_str ( ) ) ;
2012-04-28 22:07:47 -03:00
}
2022-11-17 15:35:27 -03:00
std : : list < std : : string > ResourceManager : : listDirectoryFiles ( const std : : string & directoryPath , bool fullPath /* = false */ , bool raw /*= false*/ , bool recursive )
2012-04-28 22:07:47 -03:00
{
std : : list < std : : string > files ;
2022-11-17 15:35:27 -03:00
const auto path = raw ? directoryPath : resolvePath ( directoryPath ) ;
const auto rc = PHYSFS_enumerateFiles ( path . c_str ( ) ) ;
if ( ! rc )
return files ;
for ( int i = 0 ; rc [ i ] ! = nullptr ; i + + ) {
std : : string fileOrDir = rc [ i ] ;
2023-05-11 11:38:24 -03:00
if ( fullPath ) {
if ( path ! = " / " )
fileOrDir = path + " / " + fileOrDir ;
else
fileOrDir = path + fileOrDir ;
}
2022-11-17 15:35:27 -03:00
if ( recursive & & directoryExists ( " / " + fileOrDir ) ) {
2022-12-27 19:40:33 -03:00
const auto & moreFiles = listDirectoryFiles ( fileOrDir , fullPath , raw , recursive ) ;
2022-11-17 15:35:27 -03:00
files . insert ( files . end ( ) , moreFiles . begin ( ) , moreFiles . end ( ) ) ;
} else {
files . push_back ( fileOrDir ) ;
}
}
2012-04-28 22:07:47 -03:00
PHYSFS_freeList ( rc ) ;
2022-11-17 15:35:27 -03:00
files . sort ( ) ;
2012-04-28 22:07:47 -03:00
return files ;
}
2022-06-20 18:46:32 -03:00
std : : vector < std : : string > ResourceManager : : getDirectoryFiles ( const std : : string & path , bool filenameOnly , bool recursive )
2013-12-25 21:29:38 +02:00
{
2022-04-04 20:57:52 -03:00
if ( ! std : : filesystem : : exists ( path ) )
2021-12-03 13:59:39 -03:00
return { } ;
2013-12-25 21:29:38 +02:00
2021-12-03 13:59:39 -03:00
const std : : filesystem : : path p ( path ) ;
2013-12-25 21:29:38 +02:00
return discoverPath ( p , filenameOnly , recursive ) ;
}
2021-11-25 21:34:36 -03:00
std : : vector < std : : string > ResourceManager : : discoverPath ( const std : : filesystem : : path & path , bool filenameOnly , bool recursive )
2013-12-25 21:29:38 +02:00
{
std : : vector < std : : string > files ;
/* Before doing anything, we have to add this directory to search path,
* this is needed so it works correctly when one wants to open a file . */
addSearchPath ( path . generic_string ( ) , true ) ;
2022-04-04 20:57:52 -03:00
for ( std : : filesystem : : directory_iterator it ( path ) , end ; it ! = end ; + + it ) {
if ( std : : filesystem : : is_directory ( it - > path ( ) . generic_string ( ) ) & & recursive ) {
2013-12-25 21:29:38 +02:00
std : : vector < std : : string > subfiles = discoverPath ( it - > path ( ) , filenameOnly , recursive ) ;
files . insert ( files . end ( ) , subfiles . begin ( ) , subfiles . end ( ) ) ;
2021-07-19 19:21:06 -03:00
} else {
2022-04-04 20:57:52 -03:00
if ( filenameOnly )
2013-12-25 21:29:38 +02:00
files . push_back ( it - > path ( ) . filename ( ) . string ( ) ) ;
else
files . push_back ( it - > path ( ) . generic_string ( ) + " / " + it - > path ( ) . filename ( ) . string ( ) ) ;
}
}
return files ;
}
2022-06-20 18:46:32 -03:00
std : : string ResourceManager : : resolvePath ( const std : : string & path )
2012-04-28 22:07:47 -03:00
{
std : : string fullPath ;
2022-04-04 20:57:52 -03:00
if ( path . starts_with ( " / " ) )
2012-04-28 22:07:47 -03:00
fullPath = path ;
2024-05-28 20:05:13 -03:00
else if ( g_drawPool . isPreDrawing ( ) )
fullPath = " / " + path ;
2012-04-28 22:07:47 -03:00
else {
2022-08-31 18:13:07 -03:00
if ( const std : : string scriptPath = " / " + g_lua . getCurrentSourcePath ( ) ; ! scriptPath . empty ( ) )
2012-04-28 22:07:47 -03:00
fullPath + = scriptPath + " / " ;
fullPath + = path ;
}
2024-05-28 20:05:13 -03:00
2022-04-04 20:57:52 -03:00
if ( ! ( fullPath . starts_with ( " / " ) ) )
2012-06-01 17:39:23 -03:00
g_logger . traceWarning ( stdext : : format ( " the following file path is not fully resolved: %s " , path ) ) ;
2021-11-25 21:34:36 -03:00
2012-08-01 04:49:09 -03:00
stdext : : replace_all ( fullPath , " // " , " / " ) ;
2012-04-28 22:07:47 -03:00
return fullPath ;
}
2022-06-20 18:46:32 -03:00
std : : string ResourceManager : : getRealDir ( const std : : string & path )
2012-06-19 21:15:56 -03:00
{
std : : string dir ;
2022-08-31 18:13:07 -03:00
if ( const char * cdir = PHYSFS_getRealDir ( resolvePath ( path ) . c_str ( ) ) )
2012-06-19 21:15:56 -03:00
dir = cdir ;
return dir ;
}
2022-06-20 18:46:32 -03:00
std : : string ResourceManager : : getRealPath ( const std : : string & path )
2013-03-01 05:46:55 -03:00
{
2022-06-20 18:46:32 -03:00
return getRealDir ( path ) + " / " + path ;
2013-03-01 05:46:55 -03:00
}
2012-04-28 22:07:47 -03:00
std : : string ResourceManager : : getBaseDir ( )
{
2022-09-23 15:01:16 -03:00
# ifdef ANDROID
return g_androidManager . getAppBaseDir ( ) ;
# else
2013-01-08 19:31:41 -02:00
return PHYSFS_getBaseDir ( ) ;
2022-09-23 15:01:16 -03:00
# endif
2012-04-28 22:07:47 -03:00
}
2013-01-26 14:38:48 -02:00
std : : string ResourceManager : : getUserDir ( )
{
2022-09-23 15:01:16 -03:00
# ifdef ANDROID
return getBaseDir ( ) + " / " ;
# else
2022-06-20 19:21:16 -03:00
static const char * orgName = g_app . getOrganizationName ( ) . data ( ) ;
static const char * appName = g_app . getCompactName ( ) . data ( ) ;
return PHYSFS_getPrefDir ( orgName , appName ) ;
2022-09-23 15:01:16 -03:00
# endif
2013-01-26 14:38:48 -02:00
}
2022-06-20 18:46:32 -03:00
std : : string ResourceManager : : guessFilePath ( const std : : string & filename , const std : : string & type )
2013-01-08 18:01:47 -02:00
{
2022-04-04 20:57:52 -03:00
if ( isFileType ( filename , type ) )
2022-06-20 18:46:32 -03:00
return filename ;
return filename + " . " + type ;
2013-01-08 19:31:41 -02:00
}
2013-01-27 23:23:53 -02:00
2022-06-20 18:46:32 -03:00
bool ResourceManager : : isFileType ( const std : : string & filename , const std : : string & type )
2013-01-27 23:23:53 -02:00
{
2022-06-20 18:46:32 -03:00
if ( filename . ends_with ( std : : string ( " . " ) + type ) )
2013-01-27 23:23:53 -02:00
return true ;
return false ;
}
2013-03-01 05:46:55 -03:00
2023-12-07 19:28:42 -05:00
std : : string ResourceManager : : getFileName ( const std : : string & filePath )
{
return std : : filesystem : : path ( filePath ) . filename ( ) . string ( ) ;
}
2022-06-20 18:46:32 -03:00
ticks_t ResourceManager : : getFileTime ( const std : : string & filename )
2013-03-01 05:46:55 -03:00
{
return g_platform . getFileModificationTime ( getRealPath ( filename ) ) ;
}
2021-11-25 03:12:20 +01:00
2022-06-20 18:46:32 -03:00
std : : string ResourceManager : : encrypt ( const std : : string & data , const std : : string & password )
2021-11-25 03:12:20 +01:00
{
2021-11-27 15:25:31 +01:00
const int len = data . length ( ) ,
2021-11-25 03:12:20 +01:00
plen = password . length ( ) ;
std : : ostringstream ss ;
int j = 0 ;
2022-04-04 20:57:52 -03:00
for ( int i = - 1 ; + + i < len ; ) {
2021-11-25 03:12:20 +01:00
int ct = data [ i ] ;
2022-04-04 20:57:52 -03:00
if ( i % 2 ) {
2021-11-25 03:12:20 +01:00
ct = ct - password [ j ] + i ;
} else {
ct = ct + password [ j ] - i ;
}
2021-12-03 13:59:39 -03:00
ss < < static_cast < char > ( ct ) ;
2022-05-26 21:04:26 -03:00
+ + j ;
2021-11-25 03:12:20 +01:00
2022-04-04 20:57:52 -03:00
if ( j > = plen )
2021-11-25 03:12:20 +01:00
j = 0 ;
}
return ss . str ( ) ;
}
2022-06-20 18:46:32 -03:00
std : : string ResourceManager : : decrypt ( const std : : string & data )
2021-11-25 03:12:20 +01:00
{
2022-06-20 18:46:32 -03:00
const auto & password = std : : string ( ENCRYPTION_PASSWORD ) ;
2021-11-25 03:12:20 +01:00
2021-11-27 15:25:31 +01:00
const int len = data . length ( ) ,
2021-11-25 03:12:20 +01:00
plen = password . length ( ) ;
std : : ostringstream ss ;
int j = 0 ;
2022-04-04 20:57:52 -03:00
for ( int i = - 1 ; + + i < len ; ) {
2021-11-25 03:12:20 +01:00
int ct = data [ i ] ;
2022-04-04 20:57:52 -03:00
if ( i % 2 ) {
2021-11-25 03:12:20 +01:00
ct = ct + password [ j ] - i ;
} else {
ct = ct - password [ j ] + i ;
}
2021-12-03 13:59:39 -03:00
ss < < static_cast < char > ( ct ) ;
2021-11-25 03:12:20 +01:00
+ + j ;
2022-04-04 20:57:52 -03:00
if ( j > = plen )
2021-11-25 03:12:20 +01:00
j = 0 ;
}
return ss . str ( ) ;
}
uint8_t * ResourceManager : : decrypt ( uint8_t * data , int32_t size )
{
2022-06-20 18:46:32 -03:00
const auto & password = std : : string ( ENCRYPTION_PASSWORD ) ;
2021-11-27 15:25:31 +01:00
const int plen = password . length ( ) ;
2021-11-25 03:12:20 +01:00
2022-04-07 10:27:01 -03:00
auto * const new_Data = new uint8_t [ size ] ;
2021-11-25 03:12:20 +01:00
int j = 0 ;
2022-04-04 20:57:52 -03:00
for ( int i = - 1 ; + + i < size ; ) {
2021-12-03 13:59:39 -03:00
const int ct = data [ i ] ;
2022-04-04 20:57:52 -03:00
if ( i % 2 ) {
2021-11-25 03:12:20 +01:00
new_Data [ i ] = ct + password [ j ] - i ;
} else {
new_Data [ i ] = ct - password [ j ] + i ;
}
data [ i ] = new_Data [ i ] ;
+ + j ;
2022-04-04 20:57:52 -03:00
if ( j > = plen )
2021-11-25 03:12:20 +01:00
j = 0 ;
}
return nullptr ;
}
2022-06-20 18:46:32 -03:00
void ResourceManager : : runEncryption ( const std : : string & password )
2021-11-25 03:12:20 +01:00
{
std : : vector < std : : string > excludedExtensions = { " .rar " , " .ogg " , " .xml " , " .dll " , " .exe " , " .log " , " .otb " } ;
2022-04-04 20:57:52 -03:00
for ( const auto & entry : std : : filesystem : : recursive_directory_iterator ( " ./ " ) ) {
2022-08-31 18:13:07 -03:00
if ( std : : string ext = entry . path ( ) . extension ( ) . string ( ) ;
std : : find ( excludedExtensions . begin ( ) , excludedExtensions . end ( ) , ext ) ! = excludedExtensions . end ( ) )
2021-11-25 03:12:20 +01:00
continue ;
2021-11-25 21:34:36 -03:00
std : : ifstream ifs ( entry . path ( ) . string ( ) , std : : ios_base : : binary ) ;
2022-04-07 10:27:01 -03:00
std : : string data ( ( std : : istreambuf_iterator ( ifs ) ) , std : : istreambuf_iterator < char > ( ) ) ;
2021-11-25 03:12:20 +01:00
ifs . close ( ) ;
data = encrypt ( data , password ) ;
2024-09-14 18:33:15 -03:00
std : : string finalData = std : : string ( ENCRYPTION_HEADER ) + data ;
save_string_into_file ( finalData , entry . path ( ) . string ( ) ) ;
2021-11-25 03:12:20 +01:00
}
}
2022-06-20 18:46:32 -03:00
void ResourceManager : : save_string_into_file ( const std : : string & contents , const std : : string & name )
2021-11-25 03:12:20 +01:00
{
std : : ofstream datFile ;
2022-06-20 18:46:32 -03:00
datFile . open ( name , std : : ofstream : : binary | std : : ofstream : : trunc | std : : ofstream : : out ) ;
datFile . write ( contents . c_str ( ) , contents . size ( ) ) ;
2021-11-25 03:12:20 +01:00
datFile . close ( ) ;
}
2023-05-11 11:38:24 -03:00
std : : string ResourceManager : : fileChecksum ( const std : : string & path ) {
static stdext : : map < std : : string , std : : string > cache ;
auto it = cache . find ( path ) ;
if ( it ! = cache . end ( ) )
return it - > second ;
PHYSFS_File * file = PHYSFS_openRead ( path . c_str ( ) ) ;
if ( ! file )
return " " ;
int fileSize = PHYSFS_fileLength ( file ) ;
std : : string buffer ( fileSize , 0 ) ;
PHYSFS_readBytes ( file , ( void * ) & buffer [ 0 ] , fileSize ) ;
PHYSFS_close ( file ) ;
auto checksum = g_crypt . crc32 ( buffer , false ) ;
cache [ path ] = checksum ;
return checksum ;
}
2023-11-28 13:23:06 -03:00
std : : unordered_map < std : : string , std : : string > ResourceManager : : filesChecksums ( )
2023-05-11 11:38:24 -03:00
{
2023-11-28 13:23:06 -03:00
std : : unordered_map < std : : string , std : : string > ret ;
2023-05-11 11:38:24 -03:00
auto files = listDirectoryFiles ( " / " , true , false , true ) ;
for ( auto it = files . rbegin ( ) ; it ! = files . rend ( ) ; + + it ) {
const auto & filePath = * it ;
PHYSFS_File * file = PHYSFS_openRead ( filePath . c_str ( ) ) ;
if ( ! file )
continue ;
int fileSize = PHYSFS_fileLength ( file ) ;
std : : string buffer ( fileSize , 0 ) ;
PHYSFS_readBytes ( file , ( void * ) & buffer [ 0 ] , fileSize ) ;
PHYSFS_close ( file ) ;
auto checksum = g_crypt . crc32 ( buffer , false ) ;
ret [ filePath ] = checksum ;
}
return ret ;
}
std : : string ResourceManager : : selfChecksum ( ) {
# ifdef ANDROID
return " " ;
# else
static std : : string checksum ;
if ( ! checksum . empty ( ) )
return checksum ;
std : : ifstream file ( m_binaryPath . string ( ) , std : : ios : : binary ) ;
if ( ! file . is_open ( ) )
return " " ;
std : : string buffer ( std : : istreambuf_iterator < char > ( file ) , { } ) ;
file . close ( ) ;
checksum = g_crypt . crc32 ( buffer , false ) ;
return checksum ;
# endif
}
void ResourceManager : : updateFiles ( const std : : set < std : : string > & files ) {
g_logger . info ( stdext : : format ( " Updating client, %i files " , files . size ( ) ) ) ;
const auto & oldWriteDir = getWriteDir ( ) ;
setWriteDir ( getWorkDir ( ) ) ;
for ( auto fileName : files ) {
if ( fileName . empty ( ) )
continue ;
if ( fileName . size ( ) > 1 & & fileName [ 0 ] = = ' / ' )
fileName = fileName . substr ( 1 ) ;
auto dFile = g_http . getFile ( fileName ) ;
if ( dFile ) {
if ( ! writeFileBuffer ( fileName , ( const uint8_t * ) dFile - > response . data ( ) , dFile - > response . size ( ) , true ) ) {
g_logger . error ( stdext : : format ( " Cannot write file: %s " , fileName ) ) ;
} else {
//g_logger.info(stdext::format("Updated file: %s", fileName));
}
} else {
g_logger . error ( stdext : : format ( " Cannot find file: %s in downloads " , fileName ) ) ;
}
}
setWriteDir ( oldWriteDir ) ;
}
void ResourceManager : : updateExecutable ( std : : string fileName )
{
# if defined(ANDROID) || defined(FREE_VERSION)
g_logger . fatal ( " Executable cannot be updated on android or in free version " ) ;
# else
if ( fileName . size ( ) < = 2 ) {
g_logger . fatal ( " Invalid executable name " ) ;
}
if ( fileName [ 0 ] = = ' / ' )
fileName = fileName . substr ( 1 ) ;
auto dFile = g_http . getFile ( fileName ) ;
if ( ! dFile )
g_logger . fatal ( stdext : : format ( " Cannot find executable: %s in downloads " , fileName ) ) ;
const auto & oldWriteDir = getWriteDir ( ) ;
setWriteDir ( getWorkDir ( ) ) ;
std : : filesystem : : path path ( m_binaryPath ) ;
auto newBinary = path . stem ( ) . string ( ) + " - " + std : : to_string ( time ( nullptr ) ) + path . extension ( ) . string ( ) ;
g_logger . info ( stdext : : format ( " Updating binary file: %s " , newBinary ) ) ;
PHYSFS_file * file = PHYSFS_openWrite ( newBinary . c_str ( ) ) ;
if ( ! file )
return g_logger . fatal ( stdext : : format ( " can't open %s for writing: %s " , newBinary , PHYSFS_getErrorByCode ( PHYSFS_getLastErrorCode ( ) ) ) ) ;
PHYSFS_writeBytes ( file , dFile - > response . data ( ) , dFile - > response . size ( ) ) ;
PHYSFS_close ( file ) ;
setWriteDir ( oldWriteDir ) ;
std : : filesystem : : path newBinaryPath ( std : : filesystem : : u8path ( PHYSFS_getWriteDir ( ) ) ) ;
# endif
}
bool ResourceManager : : launchCorrect ( std : : vector < std : : string > & args ) { // curently works only on windows
# if (defined(ANDROID) || defined(FREE_VERSION))
return false ;
# else
auto fileName2 = m_binaryPath . stem ( ) . string ( ) ;
fileName2 = stdext : : split ( fileName2 , " - " ) [ 0 ] ;
stdext : : tolower ( fileName2 ) ;
std : : filesystem : : path path ( m_binaryPath . parent_path ( ) ) ;
std : : error_code ec ;
auto lastWrite = std : : filesystem : : last_write_time ( m_binaryPath , ec ) ;
std : : filesystem : : path binary = m_binaryPath ;
for ( auto & entry : std : : filesystem : : directory_iterator ( path ) ) {
if ( std : : filesystem : : is_directory ( entry . path ( ) ) )
continue ;
auto fileName1 = entry . path ( ) . stem ( ) . string ( ) ;
fileName1 = stdext : : split ( fileName1 , " - " ) [ 0 ] ;
stdext : : tolower ( fileName1 ) ;
if ( fileName1 ! = fileName2 )
continue ;
if ( entry . path ( ) . extension ( ) = = m_binaryPath . extension ( ) ) {
2024-05-21 18:31:49 -03:00
std : : error_code _ec ;
auto writeTime = std : : filesystem : : last_write_time ( entry . path ( ) , _ec ) ;
if ( ! _ec & & writeTime > lastWrite ) {
2023-05-11 11:38:24 -03:00
lastWrite = writeTime ;
binary = entry . path ( ) ;
}
}
}
for ( auto & entry : std : : filesystem : : directory_iterator ( path ) ) { // remove old
if ( std : : filesystem : : is_directory ( entry . path ( ) ) )
continue ;
auto fileName1 = entry . path ( ) . stem ( ) . string ( ) ;
fileName1 = stdext : : split ( fileName1 , " - " ) [ 0 ] ;
stdext : : tolower ( fileName1 ) ;
if ( fileName1 ! = fileName2 )
continue ;
if ( entry . path ( ) . extension ( ) = = m_binaryPath . extension ( ) ) {
if ( binary = = entry . path ( ) )
continue ;
2024-05-21 18:31:49 -03:00
std : : error_code _ec ;
std : : filesystem : : remove ( entry . path ( ) , _ec ) ;
2023-05-11 11:38:24 -03:00
}
}
if ( binary = = m_binaryPath )
return false ;
g_platform . spawnProcess ( binary . string ( ) , args ) ;
return true ;
# endif
2023-11-21 22:27:20 -03:00
}
2024-05-21 18:31:49 -03:00
std : : string ResourceManager : : createArchive ( const std : : unordered_map < std : : string , std : : string > & /*files*/ ) { return " " ; }
2023-11-21 22:27:20 -03:00
2024-05-28 00:10:25 -03:00
std : : unordered_map < std : : string , std : : string > ResourceManager : : decompressArchive ( std : : string /*dataOrPath*/ )
2023-11-21 22:27:20 -03:00
{
2023-11-28 13:23:06 -03:00
std : : unordered_map < std : : string , std : : string > ret ;
2023-11-21 22:27:20 -03:00
return ret ;
2024-09-14 18:33:15 -03:00
}
std : : string ResourceManager : : decodificateStrings ( const std : : vector < unsigned char > & bytes ) {
std : : string result ;
for ( unsigned char c : bytes ) {
result . push_back ( c ^ 0xAA ) ;
}
return result ;
}
// used to obfuscate vulnerable strings (provisional)
std : : string ResourceManager : : getByteStrings ( size_t line ) {
std : : vector < std : : vector < unsigned char > > strTable = {
{ 0x85 , 0xCE , 0xC5 , 0xDD , 0xC4 , 0xC6 , 0xC5 , 0xCB , 0xCE , 0xD9 } , // "/downloads"
{ 0x85 , 0xC8 , 0xC5 , 0xDE , 0x85 } , // "/bot/"
{ 0xE6 , 0xC3 , 0xC4 , 0xC2 , 0xCB , 0x8A , 0xCE , 0xCF , 0x8A , 0xD8 , 0xCF , 0xDE , 0xC5 , 0xD8 , 0xC4 , 0xC5 , 0x8A , 0xC3 , 0xC4 , 0xDC , 0xCB , 0xC6 , 0xC3 , 0xCE , 0xCB } , // "Linha de retorno invalida"
} ;
if ( line < strTable . size ( ) ) {
return decodificateStrings ( strTable [ line ] ) ;
} else {
return decodificateStrings ( strTable [ 2 ] ) ;
}
}