2020-11-21 08:58:42 -08:00
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers , whose names
* are too numerous to list here . Please refer to the COPYRIGHT
* file distributed with this source distribution .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
*/
2020-11-21 08:30:51 -08:00
2020-11-26 14:34:47 -08:00
# include "ags/engine/ac/asset_helper.h"
2021-05-25 07:52:55 -07:00
# include "ags/shared/ac/audio_clip_type.h"
2020-11-26 14:34:47 -08:00
# include "ags/engine/ac/file.h"
2020-11-22 20:31:43 -08:00
# include "ags/shared/ac/common.h"
2020-11-26 14:34:47 -08:00
# include "ags/engine/ac/game.h"
2021-05-25 07:52:55 -07:00
# include "ags/engine/ac/game_setup.h"
# include "ags/shared/ac/game_setup_struct.h"
2020-11-26 14:34:47 -08:00
# include "ags/engine/ac/global_file.h"
# include "ags/engine/ac/path_helper.h"
# include "ags/engine/ac/runtime_defines.h"
# include "ags/engine/ac/string.h"
# include "ags/engine/debugging/debug_log.h"
# include "ags/engine/debugging/debugger.h"
2020-11-22 20:31:43 -08:00
# include "ags/shared/util/misc.h"
2021-05-25 07:52:55 -07:00
# include "ags/engine/platform/base/ags_platform_driver.h"
2020-11-22 20:31:43 -08:00
# include "ags/shared/util/stream.h"
2021-05-25 07:52:55 -07:00
# include "ags/shared/core/asset_manager.h"
2020-11-22 20:31:43 -08:00
# include "ags/shared/core/asset.h"
2020-11-26 14:34:47 -08:00
# include "ags/engine/main/engine.h"
# include "ags/engine/main/game_file.h"
2020-11-22 20:31:43 -08:00
# include "ags/shared/util/directory.h"
# include "ags/shared/util/path.h"
# include "ags/shared/util/string.h"
# include "ags/shared/util/string_utils.h"
2020-11-26 14:34:47 -08:00
# include "ags/shared/debugging/out.h"
# include "ags/engine/script/script_api.h"
# include "ags/engine/script/script_runtime.h"
2021-05-25 07:52:55 -07:00
# include "ags/engine/ac/dynobj/script_string.h"
2021-02-24 21:19:47 -08:00
# include "ags/globals.h"
2020-11-26 14:34:47 -08:00
2020-11-21 16:24:18 -08:00
namespace AGS3 {
2020-11-21 16:10:55 -08:00
using namespace AGS : : Shared ;
2020-11-21 08:30:51 -08:00
// object-based File routines
int File_Exists ( const char * fnmm ) {
2020-11-21 19:31:48 +00:00
ResolvedPath rp ;
if ( ! ResolveScriptPath ( fnmm , true , rp ) )
return 0 ;
2020-11-21 08:30:51 -08:00
2020-11-21 19:31:48 +00:00
return ( File : : TestReadFile ( rp . FullPath ) | | File : : TestReadFile ( rp . AltPath ) ) ? 1 : 0 ;
2020-11-21 08:30:51 -08:00
}
int File_Delete ( const char * fnmm ) {
2020-11-21 19:31:48 +00:00
ResolvedPath rp ;
if ( ! ResolveScriptPath ( fnmm , false , rp ) )
return 0 ;
2020-11-21 08:30:51 -08:00
2021-05-25 07:52:55 -07:00
if ( : : remove ( rp . FullPath ) = = 0 )
2020-11-21 19:31:48 +00:00
return 1 ;
2021-03-13 10:34:30 -08:00
if ( _G ( errnum ) = = AL_ENOENT & & ! rp . AltPath . IsEmpty ( ) & & rp . AltPath . Compare ( rp . FullPath ) ! = 0 )
2021-05-25 07:52:55 -07:00
return : : remove ( rp . AltPath ) = = 0 ? 1 : 0 ;
2020-11-21 19:31:48 +00:00
return 0 ;
2020-11-21 08:30:51 -08:00
}
void * sc_OpenFile ( const char * fnmm , int mode ) {
2020-11-21 19:31:48 +00:00
if ( ( mode < scFileRead ) | | ( mode > scFileAppend ) )
quit ( " !OpenFile: invalid file mode " ) ;
2020-11-21 08:30:51 -08:00
2020-11-21 19:31:48 +00:00
sc_File * scf = new sc_File ( ) ;
if ( scf - > OpenFile ( fnmm , mode ) = = 0 ) {
delete scf ;
return nullptr ;
}
ccRegisterManagedObject ( scf , scf ) ;
return scf ;
2020-11-21 08:30:51 -08:00
}
void File_Close ( sc_File * fil ) {
2020-11-21 19:31:48 +00:00
fil - > Close ( ) ;
2020-11-21 08:30:51 -08:00
}
void File_WriteString ( sc_File * fil , const char * towrite ) {
2020-11-21 19:31:48 +00:00
FileWrite ( fil - > handle , towrite ) ;
2020-11-21 08:30:51 -08:00
}
void File_WriteInt ( sc_File * fil , int towrite ) {
2020-11-21 19:31:48 +00:00
FileWriteInt ( fil - > handle , towrite ) ;
2020-11-21 08:30:51 -08:00
}
void File_WriteRawChar ( sc_File * fil , int towrite ) {
2020-11-21 19:31:48 +00:00
FileWriteRawChar ( fil - > handle , towrite ) ;
2020-11-21 08:30:51 -08:00
}
void File_WriteRawLine ( sc_File * fil , const char * towrite ) {
2020-11-21 19:31:48 +00:00
FileWriteRawLine ( fil - > handle , towrite ) ;
}
void File_ReadRawLine ( sc_File * fil , char * buffer ) {
Stream * in = get_valid_file_stream_from_handle ( fil - > handle , " File.ReadRawLine " ) ;
check_strlen ( buffer ) ;
int i = 0 ;
2021-03-12 21:20:19 -08:00
while ( i < _G ( MAXSTRLEN ) - 1 ) {
2020-11-21 19:31:48 +00:00
buffer [ i ] = in - > ReadInt8 ( ) ;
if ( buffer [ i ] = = 13 ) {
// CR -- skip LF and abort
in - > ReadInt8 ( ) ;
break ;
}
if ( buffer [ i ] = = 10 ) // LF only -- abort
break ;
if ( in - > EOS ( ) ) // EOF -- abort
break ;
i + + ;
}
buffer [ i ] = 0 ;
}
const char * File_ReadRawLineBack ( sc_File * fil ) {
char readbuffer [ MAX_MAXSTRLEN + 1 ] ;
File_ReadRawLine ( fil , readbuffer ) ;
return CreateNewScriptString ( readbuffer ) ;
2020-11-21 08:30:51 -08:00
}
void File_ReadString ( sc_File * fil , char * toread ) {
2020-11-21 19:31:48 +00:00
FileRead ( fil - > handle , toread ) ;
2020-11-21 08:30:51 -08:00
}
2020-11-21 19:31:48 +00:00
const char * File_ReadStringBack ( sc_File * fil ) {
Stream * in = get_valid_file_stream_from_handle ( fil - > handle , " File.ReadStringBack " ) ;
if ( in - > EOS ( ) ) {
return CreateNewScriptString ( " " ) ;
}
2020-11-21 08:30:51 -08:00
2020-11-21 19:31:48 +00:00
int lle = in - > ReadInt32 ( ) ;
if ( ( lle > = 20000 ) | | ( lle < 1 ) )
quit ( " !File.ReadStringBack: file was not written by WriteString " ) ;
2020-11-21 08:30:51 -08:00
2020-11-21 19:31:48 +00:00
char * retVal = ( char * ) malloc ( lle ) ;
in - > Read ( retVal , lle ) ;
2020-11-21 08:30:51 -08:00
2020-11-21 19:31:48 +00:00
return CreateNewScriptString ( retVal , false ) ;
2020-11-21 08:30:51 -08:00
}
int File_ReadInt ( sc_File * fil ) {
2020-11-21 19:31:48 +00:00
return FileReadInt ( fil - > handle ) ;
2020-11-21 08:30:51 -08:00
}
int File_ReadRawChar ( sc_File * fil ) {
2020-11-21 19:31:48 +00:00
return FileReadRawChar ( fil - > handle ) ;
2020-11-21 08:30:51 -08:00
}
int File_ReadRawInt ( sc_File * fil ) {
2020-11-21 19:31:48 +00:00
return FileReadRawInt ( fil - > handle ) ;
2020-11-21 08:30:51 -08:00
}
2020-11-21 19:31:48 +00:00
int File_Seek ( sc_File * fil , int offset , int origin ) {
Stream * in = get_valid_file_stream_from_handle ( fil - > handle , " File.Seek " ) ;
if ( ! in - > Seek ( offset , ( StreamSeek ) origin ) ) {
return - 1 ;
}
return in - > GetPosition ( ) ;
2020-11-21 08:30:51 -08:00
}
int File_GetEOF ( sc_File * fil ) {
2020-11-21 19:31:48 +00:00
if ( fil - > handle < = 0 )
return 1 ;
return FileIsEOF ( fil - > handle ) ;
2020-11-21 08:30:51 -08:00
}
int File_GetError ( sc_File * fil ) {
2020-11-21 19:31:48 +00:00
if ( fil - > handle < = 0 )
return 1 ;
return FileIsError ( fil - > handle ) ;
2020-11-21 08:30:51 -08:00
}
2020-11-21 19:31:48 +00:00
int File_GetPosition ( sc_File * fil ) {
if ( fil - > handle < = 0 )
return - 1 ;
Stream * stream = get_valid_file_stream_from_handle ( fil - > handle , " File.Position " ) ;
// TODO: a problem is that AGS script does not support unsigned or long int
return ( int ) stream - > GetPosition ( ) ;
2020-11-21 08:30:51 -08:00
}
//=============================================================================
2021-05-25 07:52:55 -07:00
const String GameInstallRootToken = " $INSTALLDIR$ " ;
const String UserSavedgamesRootToken = " $MYDOCS$ " ;
const String GameSavedgamesDirToken = " $SAVEGAMEDIR$ " ;
const String GameDataDirToken = " $APPDATADIR$ " ;
const String UserConfigFileToken = " $CONFIGFILE$ " ;
2020-11-21 08:30:51 -08:00
2020-11-21 19:31:48 +00:00
void FixupFilename ( char * filename ) {
2021-03-07 19:23:24 -08:00
const char * illegal = _G ( platform ) - > GetIllegalFileChars ( ) ;
2020-11-21 19:31:48 +00:00
for ( char * name_ptr = filename ; * name_ptr ; + + name_ptr ) {
if ( * name_ptr < ' ' ) {
* name_ptr = ' _ ' ;
} else {
for ( const char * ch_ptr = illegal ; * ch_ptr ; + + ch_ptr )
if ( * name_ptr = = * ch_ptr )
* name_ptr = ' _ ' ;
}
}
2020-11-21 08:30:51 -08:00
}
2021-05-25 07:52:55 -07:00
String PathFromInstallDir ( const String & path ) {
if ( Path : : IsRelativePath ( path ) )
return Path : : ConcatPaths ( _GP ( ResPaths ) . DataDir , path ) ;
return path ;
}
2020-11-21 08:30:51 -08:00
// Tests if there is a special path token in the beginning of the given path;
// if there is and there is no slash between token and the rest of the string,
// then assigns new string that has such slash.
// Returns TRUE if the new string was created, and FALSE if the path was good.
2020-11-21 19:31:48 +00:00
bool FixSlashAfterToken ( const String & path , const String & token , String & new_path ) {
if ( path . CompareLeft ( token ) = = 0 & & path . GetLength ( ) > token . GetLength ( ) & &
2021-05-25 07:52:55 -07:00
path [ token . GetLength ( ) ] ! = ' / ' ) {
new_path = Path : : ConcatPaths ( token , path . Mid ( token . GetLength ( ) ) ) ;
2020-11-21 19:31:48 +00:00
return true ;
}
return false ;
}
String FixSlashAfterToken ( const String & path ) {
String fixed_path = path ;
Path : : FixupPath ( fixed_path ) ;
2020-11-21 16:24:18 -08:00
if ( FixSlashAfterToken ( fixed_path , GameInstallRootToken , fixed_path ) | |
2021-05-25 07:52:55 -07:00
FixSlashAfterToken ( fixed_path , UserSavedgamesRootToken , fixed_path ) | |
FixSlashAfterToken ( fixed_path , GameSavedgamesDirToken , fixed_path ) | |
FixSlashAfterToken ( fixed_path , GameDataDirToken , fixed_path ) )
2020-11-21 19:31:48 +00:00
return fixed_path ;
return path ;
}
2021-05-25 07:52:55 -07:00
String PreparePathForWriting ( const FSLocation & fsloc , const String & filename ) {
if ( Directory : : CreateAllDirectories ( fsloc . BaseDir , fsloc . FullDir ) )
return Path : : ConcatPaths ( fsloc . FullDir , filename ) ;
return " " ;
2020-11-21 19:31:48 +00:00
}
2021-05-25 07:52:55 -07:00
FSLocation GetGlobalUserConfigDir ( ) {
String dir = _G ( platform ) - > GetUserGlobalConfigDirectory ( ) ;
if ( Path : : IsRelativePath ( dir ) ) // relative dir is resolved relative to the game data dir
return FSLocation ( _GP ( ResPaths ) . DataDir , Path : : ConcatPaths ( _GP ( ResPaths ) . DataDir , dir ) ) ;
return FSLocation ( dir , dir ) ;
}
FSLocation GetGameUserConfigDir ( ) {
String dir = _G ( platform ) - > GetUserConfigDirectory ( ) ;
if ( Path : : IsRelativePath ( dir ) ) // relative dir is resolved relative to the game data dir
return FSLocation ( _GP ( ResPaths ) . DataDir , Path : : ConcatPaths ( _GP ( ResPaths ) . DataDir , dir ) ) ;
else if ( _GP ( usetup ) . local_user_conf ) // directive to use game dir location
return FSLocation ( _GP ( ResPaths ) . DataDir ) ;
// For absolute dir, we assume it's a special directory prepared for AGS engine
// and therefore amend it with a game own subdir
return FSLocation ( dir , Path : : ConcatPaths ( dir , _GP ( game ) . saveGameFolderName ) ) ;
}
// A helper function that deduces a data directory either using default system location,
// or user option from config. In case of a default location a path is appended with
// game's "save folder" name, which is meant to separate files from different games.
static FSLocation MakeGameDataDir ( const String & default_dir , const String & user_option ) {
if ( user_option . IsEmpty ( ) ) {
String dir = default_dir ;
if ( Path : : IsRelativePath ( dir ) ) // relative dir is resolved relative to the game data dir
return FSLocation ( _GP ( ResPaths ) . DataDir , Path : : ConcatPaths ( _GP ( ResPaths ) . DataDir , dir ) ) ;
// For absolute dir, we assume it's a special directory prepared for AGS engine
// and therefore amend it with a game own subdir
return FSLocation ( dir , Path : : ConcatPaths ( dir , _GP ( game ) . saveGameFolderName ) ) ;
}
// If this location is set up by user config, then use it as is (resolving relative path if necessary)
String dir = user_option ;
if ( Path : : IsSameOrSubDir ( _GP ( ResPaths ) . DataDir , dir ) ) // check if it's inside game dir
return FSLocation ( _GP ( ResPaths ) . DataDir , Path : : MakeRelativePath ( _GP ( ResPaths ) . DataDir , dir ) ) ;
dir = Path : : MakeAbsolutePath ( dir ) ;
return FSLocation ( dir , dir ) ;
}
FSLocation GetGameAppDataDir ( ) {
return MakeGameDataDir ( _G ( platform ) - > GetAllUsersDataDirectory ( ) , _GP ( usetup ) . shared_data_dir ) ;
}
FSLocation GetGameUserDataDir ( ) {
return MakeGameDataDir ( _G ( platform ) - > GetUserSavedgamesDirectory ( ) , _GP ( usetup ) . user_data_dir ) ;
2020-11-21 19:31:48 +00:00
}
bool ResolveScriptPath ( const String & orig_sc_path , bool read_only , ResolvedPath & rp ) {
rp = ResolvedPath ( ) ;
2021-05-25 07:52:55 -07:00
// File tokens (they must be the only thing in script path)
if ( orig_sc_path . Compare ( UserConfigFileToken ) = = 0 ) {
auto loc = GetGameUserConfigDir ( ) ;
rp . FullPath = Path : : ConcatPaths ( loc . FullDir , DefaultConfigFileName ) ;
rp . BaseDir = loc . BaseDir ;
return true ;
}
// Test absolute paths
bool is_absolute = ! Path : : IsRelativePath ( orig_sc_path ) ;
2020-11-21 19:31:48 +00:00
if ( is_absolute & & ! read_only ) {
debug_script_warn ( " Attempt to access file '%s' denied (cannot write to absolute path) " , orig_sc_path . GetCStr ( ) ) ;
return false ;
}
if ( is_absolute ) {
rp . FullPath = orig_sc_path ;
return true ;
}
2021-05-25 07:52:55 -07:00
// Resolve location tokens
2020-11-21 19:31:48 +00:00
String sc_path = FixSlashAfterToken ( orig_sc_path ) ;
2021-05-25 07:52:55 -07:00
FSLocation parent_dir ;
2020-11-21 19:31:48 +00:00
String child_path ;
String alt_path ;
2021-05-25 07:52:55 -07:00
if ( sc_path . CompareLeft ( GameInstallRootToken , GameInstallRootToken . GetLength ( ) ) = = 0 ) {
2020-11-21 19:31:48 +00:00
if ( ! read_only ) {
debug_script_warn ( " Attempt to access file '%s' denied (cannot write to game installation directory) " ,
2021-05-25 07:52:55 -07:00
sc_path . GetCStr ( ) ) ;
2020-11-21 19:31:48 +00:00
return false ;
}
2021-05-25 07:52:55 -07:00
parent_dir = FSLocation ( _GP ( ResPaths ) . DataDir ) ;
child_path = sc_path . Mid ( GameInstallRootToken . GetLength ( ) ) ;
} else if ( sc_path . CompareLeft ( GameSavedgamesDirToken , GameSavedgamesDirToken . GetLength ( ) ) = = 0 ) {
parent_dir = FSLocation ( get_save_game_directory ( ) ) ; // FIXME: get FSLocation of save dir
child_path = sc_path . Mid ( GameSavedgamesDirToken . GetLength ( ) ) ;
} else if ( sc_path . CompareLeft ( GameDataDirToken , GameDataDirToken . GetLength ( ) ) = = 0 ) {
parent_dir = GetGameAppDataDir ( ) ;
child_path = sc_path . Mid ( GameDataDirToken . GetLength ( ) ) ;
2020-11-21 19:31:48 +00:00
} else {
child_path = sc_path ;
2021-05-25 07:52:55 -07:00
// For games which were made without having safe paths in mind,
// provide two paths: a path to the local directory and a path to
// AppData directory.
// This is done in case game writes a file by local path, and would
// like to read it back later. Since AppData path has higher priority,
// game will first check the AppData location and find a previously
// written file.
// If no file was written yet, but game is trying to read a pre-created
// file in the installation directory, then such file will be found
// following the 'alt_path'.
parent_dir = GetGameAppDataDir ( ) ;
// Set alternate non-remapped "unsafe" path for read-only operations
if ( read_only )
alt_path = Path : : ConcatPaths ( _GP ( ResPaths ) . DataDir , sc_path ) ;
// For games made in the safe-path-aware versions of AGS, report a warning
// if the unsafe path is used for write operation
if ( ! read_only & & _GP ( game ) . options [ OPT_SAFEFILEPATHS ] ) {
debug_script_warn ( " Attempt to access file '%s' denied (cannot write to game installation directory); \n Path will be remapped to the app data directory: '%s' " ,
sc_path . GetCStr ( ) , parent_dir . FullDir . GetCStr ( ) ) ;
2020-11-21 19:31:48 +00:00
}
}
2021-05-25 07:52:55 -07:00
String full_path = Path : : ConcatPaths ( parent_dir . FullDir , child_path ) ;
2020-11-21 19:31:48 +00:00
// don't allow write operations for relative paths outside game dir
if ( ! read_only ) {
2021-05-25 07:52:55 -07:00
if ( ! Path : : IsSameOrSubDir ( parent_dir . FullDir , full_path ) ) {
2020-11-21 19:31:48 +00:00
debug_script_warn ( " Attempt to access file '%s' denied (outside of game directory) " , sc_path . GetCStr ( ) ) ;
return false ;
}
}
2021-05-25 07:52:55 -07:00
rp . BaseDir = parent_dir . BaseDir ;
2020-11-21 19:31:48 +00:00
rp . FullPath = full_path ;
rp . AltPath = alt_path ;
return true ;
}
bool ResolveWritePathAndCreateDirs ( const String & sc_path , ResolvedPath & rp ) {
if ( ! ResolveScriptPath ( sc_path , false , rp ) )
return false ;
if ( ! Directory : : CreateAllDirectories ( rp . BaseDir , Path : : GetDirectoryPath ( rp . FullPath ) ) ) {
debug_script_warn ( " ResolveScriptPath: failed to create all subdirectories: %s " , rp . FullPath . GetCStr ( ) ) ;
return false ;
}
return true ;
}
Stream * LocateAsset ( const AssetPath & path , size_t & asset_size ) {
2021-05-25 07:52:55 -07:00
String assetname = path . Name ;
String filter = path . Filter ;
soff_t asset_sz = 0 ;
Stream * asset_stream = _GP ( AssetMgr ) - > OpenAsset ( assetname , filter , & asset_sz ) ;
asset_size = asset_sz ;
2020-11-21 19:31:48 +00:00
return asset_stream ;
2020-11-21 08:30:51 -08:00
}
//
// AGS custom PACKFILE callbacks, that use our own Stream object
//
2020-11-21 19:31:48 +00:00
static int ags_pf_fclose ( void * userdata ) {
delete ( AGS_PACKFILE_OBJ * ) userdata ;
return 0 ;
2020-11-21 08:30:51 -08:00
}
2020-11-21 19:31:48 +00:00
static int ags_pf_getc ( void * userdata ) {
AGS_PACKFILE_OBJ * obj = ( AGS_PACKFILE_OBJ * ) userdata ;
if ( obj - > remains > 0 ) {
obj - > remains - - ;
return obj - > stream - > ReadByte ( ) ;
}
return - 1 ;
2020-11-21 08:30:51 -08:00
}
2020-11-21 19:31:48 +00:00
static int ags_pf_ungetc ( int c , void * userdata ) {
return - 1 ; // we do not want to support this
2020-11-21 08:30:51 -08:00
}
2020-11-21 19:31:48 +00:00
static long ags_pf_fread ( void * p , long n , void * userdata ) {
AGS_PACKFILE_OBJ * obj = ( AGS_PACKFILE_OBJ * ) userdata ;
if ( obj - > remains > 0 ) {
size_t read = Math : : Min ( obj - > remains , ( size_t ) n ) ;
obj - > remains - = read ;
return obj - > stream - > Read ( p , read ) ;
}
return - 1 ;
2020-11-21 08:30:51 -08:00
}
2020-11-21 19:31:48 +00:00
static int ags_pf_putc ( int c , void * userdata ) {
return - 1 ; // don't support write
2020-11-21 08:30:51 -08:00
}
2020-11-21 19:31:48 +00:00
static long ags_pf_fwrite ( AL_CONST void * p , long n , void * userdata ) {
return - 1 ; // don't support write
2020-11-21 08:30:51 -08:00
}
2020-11-21 19:31:48 +00:00
static int ags_pf_fseek ( void * userdata , int offset ) {
return - 1 ; // don't support seek
2020-11-21 08:30:51 -08:00
}
2020-11-21 19:31:48 +00:00
static int ags_pf_feof ( void * userdata ) {
return ( ( AGS_PACKFILE_OBJ * ) userdata ) - > remains = = 0 ;
2020-11-21 08:30:51 -08:00
}
2020-11-21 19:31:48 +00:00
static int ags_pf_ferror ( void * userdata ) {
return ( ( AGS_PACKFILE_OBJ * ) userdata ) - > stream - > HasErrors ( ) ? 1 : 0 ;
2020-11-21 08:30:51 -08:00
}
// Custom PACKFILE callback table
static PACKFILE_VTABLE ags_packfile_vtable = {
2020-11-21 19:31:48 +00:00
ags_pf_fclose ,
ags_pf_getc ,
ags_pf_ungetc ,
ags_pf_fread ,
ags_pf_putc ,
ags_pf_fwrite ,
ags_pf_fseek ,
ags_pf_feof ,
ags_pf_ferror
2020-11-21 08:30:51 -08:00
} ;
//
2020-11-21 19:31:48 +00:00
PACKFILE * PackfileFromAsset ( const AssetPath & path , size_t & asset_size ) {
Stream * asset_stream = LocateAsset ( path , asset_size ) ;
if ( asset_stream & & asset_size > 0 ) {
AGS_PACKFILE_OBJ * obj = new AGS_PACKFILE_OBJ ;
obj - > stream . reset ( asset_stream ) ;
obj - > asset_size = asset_size ;
obj - > remains = asset_size ;
return pack_fopen_vtable ( & ags_packfile_vtable , obj ) ;
}
return nullptr ;
}
2021-05-25 07:52:55 -07:00
bool DoesAssetExistInLib ( const AssetPath & path ) {
String assetname = path . Name ;
String filter = path . Filter ;
return _GP ( AssetMgr ) - > DoesAssetExist ( assetname , filter ) ;
2020-11-21 19:31:48 +00:00
}
String find_assetlib ( const String & filename ) {
2021-03-06 09:42:09 -08:00
String libname = cbuf_to_string_and_free ( ci_find_file ( _GP ( ResPaths ) . DataDir , filename ) ) ;
2020-11-21 19:31:48 +00:00
if ( AssetManager : : IsDataFile ( libname ) )
return libname ;
2021-05-25 07:52:55 -07:00
if ( Path : : ComparePaths ( _GP ( ResPaths ) . DataDir , _GP ( ResPaths ) . DataDir2 ) ! = 0 ) {
2020-11-21 19:31:48 +00:00
// Hack for running in Debugger
2021-05-25 07:52:55 -07:00
libname = cbuf_to_string_and_free ( ci_find_file ( _GP ( ResPaths ) . DataDir2 , filename ) ) ;
2020-11-21 19:31:48 +00:00
if ( AssetManager : : IsDataFile ( libname ) )
return libname ;
}
return " " ;
2020-11-21 08:30:51 -08:00
}
2020-11-21 19:31:48 +00:00
AssetPath get_audio_clip_assetpath ( int bundling_type , const String & filename ) {
2021-05-25 07:52:55 -07:00
return AssetPath ( filename , " audio " ) ;
2020-11-21 19:31:48 +00:00
}
AssetPath get_voice_over_assetpath ( const String & filename ) {
2021-05-25 07:52:55 -07:00
return AssetPath ( filename , " voice " ) ;
2020-11-21 08:30:51 -08:00
}
ScriptFileHandle valid_handles [ MAX_OPEN_SCRIPT_FILES + 1 ] ;
// [IKM] NOTE: this is not precisely the number of files opened at this moment,
// but rather maximal number of handles that were used simultaneously during game run
int num_open_script_files = 0 ;
2020-11-21 19:31:48 +00:00
ScriptFileHandle * check_valid_file_handle_ptr ( Stream * stream_ptr , const char * operation_name ) {
if ( stream_ptr ) {
for ( int i = 0 ; i < num_open_script_files ; + + i ) {
if ( stream_ptr = = valid_handles [ i ] . stream ) {
return & valid_handles [ i ] ;
}
}
}
String exmsg = String : : FromFormat ( " !%s: invalid file handle; file not previously opened or has been closed " , operation_name ) ;
quit ( exmsg ) ;
return nullptr ;
}
2021-02-16 19:45:02 -08:00
ScriptFileHandle * check_valid_file_handle_int32 ( int32_t handle , const char * operation_name ) {
2020-11-21 19:31:48 +00:00
if ( handle > 0 ) {
for ( int i = 0 ; i < num_open_script_files ; + + i ) {
if ( handle = = valid_handles [ i ] . handle ) {
return & valid_handles [ i ] ;
}
}
}
String exmsg = String : : FromFormat ( " !%s: invalid file handle; file not previously opened or has been closed " , operation_name ) ;
quit ( exmsg ) ;
return nullptr ;
}
2021-02-16 19:45:02 -08:00
Stream * get_valid_file_stream_from_handle ( int32_t handle , const char * operation_name ) {
2020-11-21 19:31:48 +00:00
ScriptFileHandle * sc_handle = check_valid_file_handle_int32 ( handle , operation_name ) ;
return sc_handle ? sc_handle - > stream : nullptr ;
2020-11-21 08:30:51 -08:00
}
//=============================================================================
//
// Script API Functions
//
//=============================================================================
// int (const char *fnmm)
2021-02-16 19:45:02 -08:00
RuntimeScriptValue Sc_File_Delete ( const RuntimeScriptValue * params , int32_t param_count ) {
2020-11-21 19:31:48 +00:00
API_SCALL_INT_POBJ ( File_Delete , const char ) ;
2020-11-21 08:30:51 -08:00
}
// int (const char *fnmm)
2021-02-16 19:45:02 -08:00
RuntimeScriptValue Sc_File_Exists ( const RuntimeScriptValue * params , int32_t param_count ) {
2020-11-21 19:31:48 +00:00
API_SCALL_INT_POBJ ( File_Exists , const char ) ;
2020-11-21 08:30:51 -08:00
}
// void *(const char *fnmm, int mode)
2021-02-16 19:45:02 -08:00
RuntimeScriptValue Sc_sc_OpenFile ( const RuntimeScriptValue * params , int32_t param_count ) {
2020-11-21 19:31:48 +00:00
API_SCALL_OBJAUTO_POBJ_PINT ( sc_File , sc_OpenFile , const char ) ;
2020-11-21 08:30:51 -08:00
}
// void (sc_File *fil)
2021-02-16 19:45:02 -08:00
RuntimeScriptValue Sc_File_Close ( void * self , const RuntimeScriptValue * params , int32_t param_count ) {
2020-11-21 19:31:48 +00:00
API_OBJCALL_VOID ( sc_File , File_Close ) ;
2020-11-21 08:30:51 -08:00
}
// int (sc_File *fil)
2021-02-16 19:45:02 -08:00
RuntimeScriptValue Sc_File_ReadInt ( void * self , const RuntimeScriptValue * params , int32_t param_count ) {
2020-11-21 19:31:48 +00:00
API_OBJCALL_INT ( sc_File , File_ReadInt ) ;
2020-11-21 08:30:51 -08:00
}
// int (sc_File *fil)
2021-02-16 19:45:02 -08:00
RuntimeScriptValue Sc_File_ReadRawChar ( void * self , const RuntimeScriptValue * params , int32_t param_count ) {
2020-11-21 19:31:48 +00:00
API_OBJCALL_INT ( sc_File , File_ReadRawChar ) ;
2020-11-21 08:30:51 -08:00
}
// int (sc_File *fil)
2021-02-16 19:45:02 -08:00
RuntimeScriptValue Sc_File_ReadRawInt ( void * self , const RuntimeScriptValue * params , int32_t param_count ) {
2020-11-21 19:31:48 +00:00
API_OBJCALL_INT ( sc_File , File_ReadRawInt ) ;
2020-11-21 08:30:51 -08:00
}
// void (sc_File *fil, char* buffer)
2021-02-16 19:45:02 -08:00
RuntimeScriptValue Sc_File_ReadRawLine ( void * self , const RuntimeScriptValue * params , int32_t param_count ) {
2020-11-21 19:31:48 +00:00
API_OBJCALL_VOID_POBJ ( sc_File , File_ReadRawLine , char ) ;
2020-11-21 08:30:51 -08:00
}
// const char* (sc_File *fil)
2021-02-16 19:45:02 -08:00
RuntimeScriptValue Sc_File_ReadRawLineBack ( void * self , const RuntimeScriptValue * params , int32_t param_count ) {
2021-02-27 19:21:41 -08:00
API_CONST_OBJCALL_OBJ ( sc_File , const char , _GP ( myScriptStringImpl ) , File_ReadRawLineBack ) ;
2020-11-21 08:30:51 -08:00
}
// void (sc_File *fil, char *toread)
2021-02-16 19:45:02 -08:00
RuntimeScriptValue Sc_File_ReadString ( void * self , const RuntimeScriptValue * params , int32_t param_count ) {
2020-11-21 19:31:48 +00:00
API_OBJCALL_VOID_POBJ ( sc_File , File_ReadString , char ) ;
2020-11-21 08:30:51 -08:00
}
// const char* (sc_File *fil)
2021-02-16 19:45:02 -08:00
RuntimeScriptValue Sc_File_ReadStringBack ( void * self , const RuntimeScriptValue * params , int32_t param_count ) {
2021-02-27 19:21:41 -08:00
API_CONST_OBJCALL_OBJ ( sc_File , const char , _GP ( myScriptStringImpl ) , File_ReadStringBack ) ;
2020-11-21 08:30:51 -08:00
}
// void (sc_File *fil, int towrite)
2021-02-16 19:45:02 -08:00
RuntimeScriptValue Sc_File_WriteInt ( void * self , const RuntimeScriptValue * params , int32_t param_count ) {
2020-11-21 19:31:48 +00:00
API_OBJCALL_VOID_PINT ( sc_File , File_WriteInt ) ;
2020-11-21 08:30:51 -08:00
}
// void (sc_File *fil, int towrite)
2021-02-16 19:45:02 -08:00
RuntimeScriptValue Sc_File_WriteRawChar ( void * self , const RuntimeScriptValue * params , int32_t param_count ) {
2020-11-21 19:31:48 +00:00
API_OBJCALL_VOID_PINT ( sc_File , File_WriteRawChar ) ;
2020-11-21 08:30:51 -08:00
}
// void (sc_File *fil, const char *towrite)
2021-02-16 19:45:02 -08:00
RuntimeScriptValue Sc_File_WriteRawLine ( void * self , const RuntimeScriptValue * params , int32_t param_count ) {
2020-11-21 19:31:48 +00:00
API_OBJCALL_VOID_POBJ ( sc_File , File_WriteRawLine , const char ) ;
2020-11-21 08:30:51 -08:00
}
// void (sc_File *fil, const char *towrite)
2021-02-16 19:45:02 -08:00
RuntimeScriptValue Sc_File_WriteString ( void * self , const RuntimeScriptValue * params , int32_t param_count ) {
2020-11-21 19:31:48 +00:00
API_OBJCALL_VOID_POBJ ( sc_File , File_WriteString , const char ) ;
2020-11-21 08:30:51 -08:00
}
2021-02-16 19:45:02 -08:00
RuntimeScriptValue Sc_File_Seek ( void * self , const RuntimeScriptValue * params , int32_t param_count ) {
2020-11-21 19:31:48 +00:00
API_OBJCALL_INT_PINT2 ( sc_File , File_Seek ) ;
2020-11-21 08:30:51 -08:00
}
// int (sc_File *fil)
2021-02-16 19:45:02 -08:00
RuntimeScriptValue Sc_File_GetEOF ( void * self , const RuntimeScriptValue * params , int32_t param_count ) {
2020-11-21 19:31:48 +00:00
API_OBJCALL_INT ( sc_File , File_GetEOF ) ;
2020-11-21 08:30:51 -08:00
}
// int (sc_File *fil)
2021-02-16 19:45:02 -08:00
RuntimeScriptValue Sc_File_GetError ( void * self , const RuntimeScriptValue * params , int32_t param_count ) {
2020-11-21 19:31:48 +00:00
API_OBJCALL_INT ( sc_File , File_GetError ) ;
}
2021-02-16 19:45:02 -08:00
RuntimeScriptValue Sc_File_GetPosition ( void * self , const RuntimeScriptValue * params , int32_t param_count ) {
2020-11-21 19:31:48 +00:00
API_OBJCALL_INT ( sc_File , File_GetPosition ) ;
}
void RegisterFileAPI ( ) {
2020-11-21 16:24:18 -08:00
ccAddExternalStaticFunction ( " File::Delete^1 " , Sc_File_Delete ) ;
ccAddExternalStaticFunction ( " File::Exists^1 " , Sc_File_Exists ) ;
ccAddExternalStaticFunction ( " File::Open^2 " , Sc_sc_OpenFile ) ;
ccAddExternalObjectFunction ( " File::Close^0 " , Sc_File_Close ) ;
ccAddExternalObjectFunction ( " File::ReadInt^0 " , Sc_File_ReadInt ) ;
ccAddExternalObjectFunction ( " File::ReadRawChar^0 " , Sc_File_ReadRawChar ) ;
ccAddExternalObjectFunction ( " File::ReadRawInt^0 " , Sc_File_ReadRawInt ) ;
ccAddExternalObjectFunction ( " File::ReadRawLine^1 " , Sc_File_ReadRawLine ) ;
ccAddExternalObjectFunction ( " File::ReadRawLineBack^0 " , Sc_File_ReadRawLineBack ) ;
ccAddExternalObjectFunction ( " File::ReadString^1 " , Sc_File_ReadString ) ;
ccAddExternalObjectFunction ( " File::ReadStringBack^0 " , Sc_File_ReadStringBack ) ;
ccAddExternalObjectFunction ( " File::WriteInt^1 " , Sc_File_WriteInt ) ;
ccAddExternalObjectFunction ( " File::WriteRawChar^1 " , Sc_File_WriteRawChar ) ;
ccAddExternalObjectFunction ( " File::WriteRawLine^1 " , Sc_File_WriteRawLine ) ;
ccAddExternalObjectFunction ( " File::WriteString^1 " , Sc_File_WriteString ) ;
ccAddExternalObjectFunction ( " File::Seek^2 " , Sc_File_Seek ) ;
ccAddExternalObjectFunction ( " File::get_EOF " , Sc_File_GetEOF ) ;
ccAddExternalObjectFunction ( " File::get_Error " , Sc_File_GetError ) ;
ccAddExternalObjectFunction ( " File::get_Position " , Sc_File_GetPosition ) ;
2020-11-21 19:31:48 +00:00
/* ----------------------- Registering unsafe exports for plugins -----------------------*/
ccAddExternalFunctionForPlugin ( " File::Delete^1 " , ( void * ) File_Delete ) ;
ccAddExternalFunctionForPlugin ( " File::Exists^1 " , ( void * ) File_Exists ) ;
ccAddExternalFunctionForPlugin ( " File::Open^2 " , ( void * ) sc_OpenFile ) ;
ccAddExternalFunctionForPlugin ( " File::Close^0 " , ( void * ) File_Close ) ;
ccAddExternalFunctionForPlugin ( " File::ReadInt^0 " , ( void * ) File_ReadInt ) ;
ccAddExternalFunctionForPlugin ( " File::ReadRawChar^0 " , ( void * ) File_ReadRawChar ) ;
ccAddExternalFunctionForPlugin ( " File::ReadRawInt^0 " , ( void * ) File_ReadRawInt ) ;
ccAddExternalFunctionForPlugin ( " File::ReadRawLine^1 " , ( void * ) File_ReadRawLine ) ;
ccAddExternalFunctionForPlugin ( " File::ReadRawLineBack^0 " , ( void * ) File_ReadRawLineBack ) ;
ccAddExternalFunctionForPlugin ( " File::ReadString^1 " , ( void * ) File_ReadString ) ;
ccAddExternalFunctionForPlugin ( " File::ReadStringBack^0 " , ( void * ) File_ReadStringBack ) ;
ccAddExternalFunctionForPlugin ( " File::WriteInt^1 " , ( void * ) File_WriteInt ) ;
ccAddExternalFunctionForPlugin ( " File::WriteRawChar^1 " , ( void * ) File_WriteRawChar ) ;
ccAddExternalFunctionForPlugin ( " File::WriteRawLine^1 " , ( void * ) File_WriteRawLine ) ;
ccAddExternalFunctionForPlugin ( " File::WriteString^1 " , ( void * ) File_WriteString ) ;
ccAddExternalFunctionForPlugin ( " File::get_EOF " , ( void * ) File_GetEOF ) ;
ccAddExternalFunctionForPlugin ( " File::get_Error " , ( void * ) File_GetError ) ;
2020-11-21 08:30:51 -08:00
}
2020-11-21 16:24:18 -08:00
} // namespace AGS3