1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "common/config-manager.h"
24 #include "ags/engine/ac/asset_helper.h"
25 #include "ags/shared/ac/audio_clip_type.h"
26 #include "ags/engine/ac/file.h"
27 #include "ags/shared/ac/common.h"
28 #include "ags/engine/ac/game.h"
29 #include "ags/engine/ac/game_setup.h"
30 #include "ags/shared/ac/game_setup_struct.h"
31 #include "ags/engine/ac/global_file.h"
32 #include "ags/engine/ac/path_helper.h"
33 #include "ags/engine/ac/runtime_defines.h"
34 #include "ags/engine/ac/string.h"
35 #include "ags/engine/debugging/debug_log.h"
36 #include "ags/engine/debugging/debugger.h"
37 #include "ags/engine/platform/base/ags_platform_driver.h"
38 #include "ags/shared/util/stream.h"
39 #include "ags/shared/core/asset_manager.h"
40 #include "ags/shared/core/asset.h"
41 #include "ags/engine/main/engine.h"
42 #include "ags/engine/main/game_file.h"
43 #include "ags/shared/util/directory.h"
44 #include "ags/shared/util/path.h"
45 #include "ags/shared/util/string.h"
46 #include "ags/shared/util/string_utils.h"
47 #include "ags/shared/debugging/out.h"
48 #include "ags/engine/script/script_api.h"
49 #include "ags/engine/script/script_runtime.h"
50 #include "ags/engine/ac/dynobj/script_string.h"
51 #include "ags/ags.h"
52 #include "ags/globals.h"
53 
54 namespace AGS3 {
55 
56 using namespace AGS::Shared;
57 
58 // object-based File routines
59 
File_Exists(const char * fnmm)60 int File_Exists(const char *fnmm) {
61 
62 	ResolvedPath rp;
63 	if (!ResolveScriptPath(fnmm, true, rp))
64 		return 0;
65 
66 	return (File::TestReadFile(rp.FullPath) || File::TestReadFile(rp.AltPath)) ? 1 : 0;
67 }
68 
File_Delete(const char * fnmm)69 int File_Delete(const char *fnmm) {
70 
71 	ResolvedPath rp;
72 	if (!ResolveScriptPath(fnmm, false, rp))
73 		return 0;
74 
75 	if (File::DeleteFile(rp.FullPath))
76 		return 1;
77 	if (_G(errnum) == AL_ENOENT && !rp.AltPath.IsEmpty() && rp.AltPath.Compare(rp.FullPath) != 0)
78 		return File::DeleteFile(rp.AltPath) ? 1 : 0;
79 	return 0;
80 }
81 
sc_OpenFile(const char * fnmm,int mode)82 void *sc_OpenFile(const char *fnmm, int mode) {
83 	if ((mode < scFileRead) || (mode > scFileAppend))
84 		quit("!OpenFile: invalid file mode");
85 
86 	sc_File *scf = new sc_File();
87 	if (scf->OpenFile(fnmm, mode) == 0) {
88 		delete scf;
89 		return nullptr;
90 	}
91 	ccRegisterManagedObject(scf, scf);
92 	return scf;
93 }
94 
File_Close(sc_File * fil)95 void File_Close(sc_File *fil) {
96 	fil->Close();
97 }
98 
File_WriteString(sc_File * fil,const char * towrite)99 void File_WriteString(sc_File *fil, const char *towrite) {
100 	FileWrite(fil->handle, towrite);
101 }
102 
File_WriteInt(sc_File * fil,int towrite)103 void File_WriteInt(sc_File *fil, int towrite) {
104 	FileWriteInt(fil->handle, towrite);
105 }
106 
File_WriteRawChar(sc_File * fil,int towrite)107 void File_WriteRawChar(sc_File *fil, int towrite) {
108 	FileWriteRawChar(fil->handle, towrite);
109 }
110 
File_WriteRawLine(sc_File * fil,const char * towrite)111 void File_WriteRawLine(sc_File *fil, const char *towrite) {
112 	FileWriteRawLine(fil->handle, towrite);
113 }
114 
File_ReadRawLine(sc_File * fil,char * buffer)115 void File_ReadRawLine(sc_File *fil, char *buffer) {
116 	Stream *in = get_valid_file_stream_from_handle(fil->handle, "File.ReadRawLine");
117 	check_strlen(buffer);
118 	int i = 0;
119 	while (i < _G(MAXSTRLEN) - 1) {
120 		buffer[i] = in->ReadInt8();
121 		if (buffer[i] == '\r') {
122 			// CR -- skip LF and abort
123 			in->ReadInt8();
124 			break;
125 		}
126 		if (buffer[i] == '\n')  // LF only -- abort
127 			break;
128 		if (in->EOS())  // EOF -- abort
129 			break;
130 		i++;
131 	}
132 	buffer[i] = 0;
133 }
134 
File_ReadRawLineBack(sc_File * fil)135 const char *File_ReadRawLineBack(sc_File *fil) {
136 	char readbuffer[MAX_MAXSTRLEN + 1];
137 	File_ReadRawLine(fil, readbuffer);
138 	return CreateNewScriptString(readbuffer);
139 }
140 
File_ReadString(sc_File * fil,char * toread)141 void File_ReadString(sc_File *fil, char *toread) {
142 	FileRead(fil->handle, toread);
143 }
144 
File_ReadStringBack(sc_File * fil)145 const char *File_ReadStringBack(sc_File *fil) {
146 	Stream *in = get_valid_file_stream_from_handle(fil->handle, "File.ReadStringBack");
147 	if (in->EOS()) {
148 		return CreateNewScriptString("");
149 	}
150 
151 	size_t lle = (uint32_t)in->ReadInt32();
152 	if (lle == 0) {
153 		debug_script_warn("File.ReadStringBack: file was not written by WriteString");
154 		return CreateNewScriptString("");
155 	}
156 
157 	char *retVal = (char *)malloc(lle);
158 	in->Read(retVal, lle);
159 
160 	return CreateNewScriptString(retVal, false);
161 }
162 
File_ReadInt(sc_File * fil)163 int File_ReadInt(sc_File *fil) {
164 	return FileReadInt(fil->handle);
165 }
166 
File_ReadRawChar(sc_File * fil)167 int File_ReadRawChar(sc_File *fil) {
168 	return FileReadRawChar(fil->handle);
169 }
170 
File_ReadRawInt(sc_File * fil)171 int File_ReadRawInt(sc_File *fil) {
172 	return FileReadRawInt(fil->handle);
173 }
174 
File_Seek(sc_File * fil,int offset,int origin)175 int File_Seek(sc_File *fil, int offset, int origin) {
176 	Stream *in = get_valid_file_stream_from_handle(fil->handle, "File.Seek");
177 	if (!in->Seek(offset, (StreamSeek)origin)) {
178 		return -1;
179 	}
180 	return in->GetPosition();
181 }
182 
File_GetEOF(sc_File * fil)183 int File_GetEOF(sc_File *fil) {
184 	if (fil->handle <= 0)
185 		return 1;
186 	return FileIsEOF(fil->handle);
187 }
188 
File_GetError(sc_File * fil)189 int File_GetError(sc_File *fil) {
190 	if (fil->handle <= 0)
191 		return 1;
192 	return FileIsError(fil->handle);
193 }
194 
File_GetPosition(sc_File * fil)195 int File_GetPosition(sc_File *fil) {
196 	if (fil->handle <= 0)
197 		return -1;
198 	Stream *stream = get_valid_file_stream_from_handle(fil->handle, "File.Position");
199 	// TODO: a problem is that AGS script does not support unsigned or long int
200 	return (int)stream->GetPosition();
201 }
202 
203 //=============================================================================
204 
205 
206 const char *GameInstallRootToken = "$INSTALLDIR$";
207 const char *UserSavedgamesRootToken = "$MYDOCS$";
208 const char *GameSavedgamesDirToken = "$SAVEGAMEDIR$";
209 const char *GameDataDirToken = "$APPDATADIR$";
210 const char *UserConfigFileToken = "$CONFIGFILE$";
211 
FixupFilename(char * filename)212 void FixupFilename(char *filename) {
213 	const char *illegal = _G(platform)->GetIllegalFileChars();
214 	for (char *name_ptr = filename; *name_ptr; ++name_ptr) {
215 		if (*name_ptr < ' ') {
216 			*name_ptr = '_';
217 		} else {
218 			for (const char *ch_ptr = illegal; *ch_ptr; ++ch_ptr)
219 				if (*name_ptr == *ch_ptr)
220 					*name_ptr = '_';
221 		}
222 	}
223 }
224 
PathFromInstallDir(const String & path)225 String PathFromInstallDir(const String &path) {
226 	if (Path::IsRelativePath(path))
227 		return Path::ConcatPaths(_GP(ResPaths).DataDir, path);
228 	return path;
229 }
230 
PreparePathForWriting(const FSLocation & fsloc,const String & filename)231 String PreparePathForWriting(const FSLocation &fsloc, const String &filename) {
232 	if (Directory::CreateAllDirectories(fsloc.BaseDir, fsloc.SubDir))
233 		return Path::ConcatPaths(fsloc.FullDir, filename);
234 	return "";
235 }
236 
GetGlobalUserConfigDir()237 FSLocation GetGlobalUserConfigDir() {
238 	String dir = _G(platform)->GetUserGlobalConfigDirectory();
239 	if (Path::IsRelativePath(dir)) // relative dir is resolved relative to the game data dir
240 		return FSLocation(_GP(ResPaths).DataDir, dir);
241 	return FSLocation(dir);
242 }
243 
GetGameUserConfigDir()244 FSLocation GetGameUserConfigDir() {
245 	String dir = _G(platform)->GetUserConfigDirectory();
246 	if (Path::IsRelativePath(dir)) // relative dir is resolved relative to the game data dir
247 		return FSLocation(_GP(ResPaths).DataDir, dir);
248 	else if (_GP(usetup).local_user_conf) // directive to use game dir location
249 		return FSLocation(_GP(ResPaths).DataDir);
250 	// For absolute dir, we assume it's a special directory prepared for AGS engine
251 	// and therefore amend it with a game own subdir
252 	return FSLocation(dir, _GP(game).saveGameFolderName);
253 }
254 
255 // A helper function that deduces a data directory either using default system location,
256 // or user option from config. In case of a default location a path is appended with
257 // game's "save folder" name, which is meant to separate files from different games.
MakeGameDataDir(const String & default_dir,const String & user_option)258 static FSLocation MakeGameDataDir(const String &default_dir, const String &user_option) {
259 	if (user_option.IsEmpty()) {
260 		String dir = default_dir;
261 		if (Path::IsRelativePath(dir)) // relative dir is resolved relative to the game data dir
262 			return FSLocation(_GP(ResPaths).DataDir, dir);
263 		// For absolute dir, we assume it's a special directory prepared for AGS engine
264 		// and therefore amend it with a game own subdir
265 		return FSLocation(dir, _GP(game).saveGameFolderName);
266 	}
267 	// If this location is set up by user config, then use it as is (resolving relative path if necessary)
268 	String dir = user_option;
269 	if (Path::IsSameOrSubDir(_GP(ResPaths).DataDir, dir)) // check if it's inside game dir
270 		return FSLocation(_GP(ResPaths).DataDir, Path::MakeRelativePath(_GP(ResPaths).DataDir, dir));
271 	dir = Path::MakeAbsolutePath(dir);
272 	return FSLocation(dir);
273 }
274 
GetGameAppDataDir()275 FSLocation GetGameAppDataDir() {
276 	return MakeGameDataDir(_G(platform)->GetAllUsersDataDirectory(), _GP(usetup).shared_data_dir);
277 }
278 
GetGameUserDataDir()279 FSLocation GetGameUserDataDir() {
280 	return MakeGameDataDir(_G(platform)->GetUserSavedgamesDirectory(), _GP(usetup).user_data_dir);
281 }
282 
ResolveScriptPath(const String & orig_sc_path,bool read_only,ResolvedPath & rp)283 bool ResolveScriptPath(const String &orig_sc_path, bool read_only, ResolvedPath &rp) {
284 	debugC(::AGS::kDebugFilePath, "ResolveScriptPath(%s)", orig_sc_path.GetCStr());
285 	rp = ResolvedPath();
286 
287 	bool is_absolute = !is_relative_filename(orig_sc_path.GetCStr());
288 	if (is_absolute && !read_only) {
289 		debug_script_warn("Attempt to access file '%s' denied (cannot write to absolute path)", orig_sc_path.GetCStr());
290 		return false;
291 	}
292 
293 	if (is_absolute) {
294 		rp = ResolvedPath(orig_sc_path);
295 		return true;
296 	}
297 
298 	String sc_path = orig_sc_path;
299 	FSLocation parent_dir;
300 	String child_path;
301 	String alt_path;
302 	if (sc_path.CompareLeft(GameInstallRootToken) == 0) {
303 		if (!read_only) {
304 			debug_script_warn("Attempt to access file '%s' denied (cannot write to game installation directory)",
305 				sc_path.GetCStr());
306 			return false;
307 		}
308 		parent_dir = FSLocation(_GP(ResPaths).DataDir);
309 		child_path = sc_path.Mid(strlen(GameInstallRootToken));
310 	} else if (sc_path.CompareLeft(GameSavedgamesDirToken) == 0) {
311 		parent_dir = get_save_game_directory();
312 		child_path = sc_path.Mid(strlen(GameSavedgamesDirToken));
313 #if AGS_PLATFORM_SCUMMVM
314 		// Remap "agsgame.*"
315 		const char *agsSavePrefix = "/agssave.";
316 		if (child_path.CompareLeft(agsSavePrefix) == 0) {
317 			debugC(::AGS::kDebugFilePath, "Remapping agssave.* to ScummVM savegame files");
318 			String suffix = child_path.Mid(strlen(agsSavePrefix));
319 			if (suffix.CompareLeft("*") == 0) {
320 				Common::String file_name = ::AGS::g_vm->getSaveStateName(999);
321 				Common::replace(file_name, "999", "*");
322 				child_path = file_name;
323 			} else {
324 				int slotNum = suffix.ToInt();
325 				child_path = ::AGS::g_vm->getSaveStateName(slotNum);
326 			}
327 		}
328 #endif
329 	} else if (sc_path.CompareLeft(GameDataDirToken) == 0) {
330 		parent_dir = GetGameAppDataDir();
331 		child_path = sc_path.Mid(strlen(GameDataDirToken));
332 	} else {
333 		child_path = sc_path;
334 
335 		// For cases where a file is trying to write to a game path, always remap
336 		// it to write to a savefile. For normal reading, we thus need to give
337 		// preference to any save file with a given name before looking in the
338 		// game folder. This for example fixes an issue with The Blackwell Legacy,
339 		// which wants to create a new prog.bwl in the game folder
340 		parent_dir = FSLocation(SAVE_FOLDER_PREFIX);
341 
342 		if (read_only)
343 			alt_path = Path::ConcatPaths(_GP(ResPaths).DataDir, sc_path);
344 	}
345 
346 	// Sometimes we have multiple consecutive slashes or backslashes.
347 	// Remove all of them at the start of the child path.
348 	while (!child_path.IsEmpty() && (child_path[0u] == '\\' || child_path[0u] == '/'))
349 		child_path.ClipLeft(1);
350 
351 #if AGS_PLATFORM_SCUMMVM
352 	// For files on savepath, always ensure it starts with the game target prefix to avoid
353 	// conflicts (as we usually have the same save dir for all games).
354 	// Also flatten the path if needed as we do not support subdirectories in the save folder.
355 	if (parent_dir.BaseDir == SAVE_FOLDER_PREFIX) {
356 		debugC(::AGS::kDebugFilePath, "Adding ScummVM game target prefix and flatten path");
357 		child_path.Replace('/', '-');
358 		String gameTarget = ConfMan.getActiveDomainName();
359 		if (child_path.CompareLeftNoCase(gameTarget) != 0)
360 			child_path = String::FromFormat("%s-%s", gameTarget.GetCStr(), child_path.GetCStr());
361 	}
362 #endif
363 
364 	// don't allow write operations for relative paths outside game dir
365 	ResolvedPath test_rp = ResolvedPath(parent_dir, child_path, alt_path);
366 	if (!read_only) {
367 		if (!Path::IsSameOrSubDir(test_rp.Loc.FullDir, test_rp.FullPath)) {
368 			debug_script_warn("Attempt to access file '%s' denied (outside of game directory)", sc_path.GetCStr());
369 			return false;
370 		}
371 	}
372 
373 	rp = test_rp;
374 	debugC(::AGS::kDebugFilePath, "Final path: %s", rp.FullPath.GetCStr());
375 	if (!rp.AltPath.IsEmpty())
376 		debugC(::AGS::kDebugFilePath, "Alt path: %s", rp.AltPath.GetCStr());
377 	return true;
378 }
379 
ResolveWritePathAndCreateDirs(const String & sc_path,ResolvedPath & rp)380 bool ResolveWritePathAndCreateDirs(const String &sc_path, ResolvedPath &rp) {
381 	if (!ResolveScriptPath(sc_path, false, rp))
382 		return false;
383 
384 	if (!rp.Loc.SubDir.IsEmpty() &&
385 			!Directory::CreateAllDirectories(rp.Loc.BaseDir, rp.Loc.SubDir)) {
386 		debug_script_warn("ResolveScriptPath: failed to create all subdirectories: %s", rp.FullPath.GetCStr());
387 		return false;
388 	}
389 	return true;
390 }
391 
LocateAsset(const AssetPath & path,size_t & asset_size)392 Stream *LocateAsset(const AssetPath &path, size_t &asset_size) {
393 	String assetname = path.Name;
394 	String filter = path.Filter;
395 	soff_t asset_sz = 0;
396 	Stream *asset_stream = _GP(AssetMgr)->OpenAsset(assetname, filter, &asset_sz);
397 	asset_size = asset_sz;
398 	return asset_stream;
399 }
400 
401 //
402 // AGS custom PACKFILE callbacks, that use our own Stream object
403 //
ags_pf_fclose(void * userdata)404 static int ags_pf_fclose(void *userdata) {
405 	delete(AGS_PACKFILE_OBJ *)userdata;
406 	return 0;
407 }
408 
ags_pf_getc(void * userdata)409 static int ags_pf_getc(void *userdata) {
410 	AGS_PACKFILE_OBJ *obj = (AGS_PACKFILE_OBJ *)userdata;
411 	if (obj->remains > 0) {
412 		obj->remains--;
413 		return obj->stream->ReadByte();
414 	}
415 	return -1;
416 }
417 
ags_pf_ungetc(int c,void * userdata)418 static int ags_pf_ungetc(int c, void *userdata) {
419 	return -1; // we do not want to support this
420 }
421 
ags_pf_fread(void * p,long n,void * userdata)422 static long ags_pf_fread(void *p, long n, void *userdata) {
423 	AGS_PACKFILE_OBJ *obj = (AGS_PACKFILE_OBJ *)userdata;
424 	if (obj->remains > 0) {
425 		size_t read = Math::Min(obj->remains, (size_t)n);
426 		obj->remains -= read;
427 		return obj->stream->Read(p, read);
428 	}
429 	return -1;
430 }
431 
ags_pf_putc(int c,void * userdata)432 static int ags_pf_putc(int c, void *userdata) {
433 	return -1;  // don't support write
434 }
435 
ags_pf_fwrite(AL_CONST void * p,long n,void * userdata)436 static long ags_pf_fwrite(AL_CONST void *p, long n, void *userdata) {
437 	return -1; // don't support write
438 }
439 
ags_pf_fseek(void * userdata,int offset)440 static int ags_pf_fseek(void *userdata, int offset) {
441 	return -1; // don't support seek
442 }
443 
ags_pf_feof(void * userdata)444 static int ags_pf_feof(void *userdata) {
445 	return ((AGS_PACKFILE_OBJ *)userdata)->remains == 0;
446 }
447 
ags_pf_ferror(void * userdata)448 static int ags_pf_ferror(void *userdata) {
449 	return ((AGS_PACKFILE_OBJ *)userdata)->stream->HasErrors() ? 1 : 0;
450 }
451 
452 // Custom PACKFILE callback table
453 static PACKFILE_VTABLE ags_packfile_vtable = {
454 	ags_pf_fclose,
455 	ags_pf_getc,
456 	ags_pf_ungetc,
457 	ags_pf_fread,
458 	ags_pf_putc,
459 	ags_pf_fwrite,
460 	ags_pf_fseek,
461 	ags_pf_feof,
462 	ags_pf_ferror
463 };
464 //
465 
PackfileFromAsset(const AssetPath & path,size_t & asset_size)466 PACKFILE *PackfileFromAsset(const AssetPath &path, size_t &asset_size) {
467 	Stream *asset_stream = LocateAsset(path, asset_size);
468 	if (asset_stream && asset_size > 0) {
469 		AGS_PACKFILE_OBJ *obj = new AGS_PACKFILE_OBJ;
470 		obj->stream.reset(asset_stream);
471 		obj->asset_size = asset_size;
472 		obj->remains = asset_size;
473 		return pack_fopen_vtable(&ags_packfile_vtable, obj);
474 	}
475 	return nullptr;
476 }
477 
DoesAssetExistInLib(const AssetPath & path)478 bool DoesAssetExistInLib(const AssetPath &path) {
479 	String assetname = path.Name;
480 	String filter = path.Filter;
481 	return _GP(AssetMgr)->DoesAssetExist(assetname, filter);
482 }
483 
find_assetlib(const String & filename)484 String find_assetlib(const String &filename) {
485 	String libname = File::FindFileCI(_GP(ResPaths).DataDir, filename);
486 	if (AssetManager::IsDataFile(libname))
487 		return libname;
488 	if (Path::ComparePaths(_GP(ResPaths).DataDir, _GP(ResPaths).DataDir2) != 0) {
489 		// Hack for running in Debugger
490 		libname = File::FindFileCI(_GP(ResPaths).DataDir2, filename);
491 		if (AssetManager::IsDataFile(libname))
492 			return libname;
493 	}
494 	return "";
495 }
496 
get_audio_clip_assetpath(int bundling_type,const String & filename)497 AssetPath get_audio_clip_assetpath(int bundling_type, const String &filename) {
498 	return AssetPath(filename, "audio");
499 }
500 
get_voice_over_assetpath(const String & filename)501 AssetPath get_voice_over_assetpath(const String &filename) {
502 	return AssetPath(filename, "voice");
503 }
504 
505 ScriptFileHandle valid_handles[MAX_OPEN_SCRIPT_FILES + 1];
506 // [IKM] NOTE: this is not precisely the number of files opened at this moment,
507 // but rather maximal number of handles that were used simultaneously during game run
508 int num_open_script_files = 0;
check_valid_file_handle_ptr(Stream * stream_ptr,const char * operation_name)509 ScriptFileHandle *check_valid_file_handle_ptr(Stream *stream_ptr, const char *operation_name) {
510 	if (stream_ptr) {
511 		for (int i = 0; i < num_open_script_files; ++i) {
512 			if (stream_ptr == valid_handles[i].stream) {
513 				return &valid_handles[i];
514 			}
515 		}
516 	}
517 
518 	String exmsg = String::FromFormat("!%s: invalid file handle; file not previously opened or has been closed", operation_name);
519 	quit(exmsg);
520 	return nullptr;
521 }
522 
check_valid_file_handle_int32(int32_t handle,const char * operation_name)523 ScriptFileHandle *check_valid_file_handle_int32(int32_t handle, const char *operation_name) {
524 	if (handle > 0) {
525 		for (int i = 0; i < num_open_script_files; ++i) {
526 			if (handle == valid_handles[i].handle) {
527 				return &valid_handles[i];
528 			}
529 		}
530 	}
531 
532 	String exmsg = String::FromFormat("!%s: invalid file handle; file not previously opened or has been closed", operation_name);
533 	quit(exmsg);
534 	return nullptr;
535 }
536 
get_valid_file_stream_from_handle(int32_t handle,const char * operation_name)537 Stream *get_valid_file_stream_from_handle(int32_t handle, const char *operation_name) {
538 	ScriptFileHandle *sc_handle = check_valid_file_handle_int32(handle, operation_name);
539 	return sc_handle ? sc_handle->stream : nullptr;
540 }
541 
542 //=============================================================================
543 //
544 // Script API Functions
545 //
546 //=============================================================================
547 
548 // int (const char *fnmm)
Sc_File_Delete(const RuntimeScriptValue * params,int32_t param_count)549 RuntimeScriptValue Sc_File_Delete(const RuntimeScriptValue *params, int32_t param_count) {
550 	API_SCALL_INT_POBJ(File_Delete, const char);
551 }
552 
553 // int (const char *fnmm)
Sc_File_Exists(const RuntimeScriptValue * params,int32_t param_count)554 RuntimeScriptValue Sc_File_Exists(const RuntimeScriptValue *params, int32_t param_count) {
555 	API_SCALL_INT_POBJ(File_Exists, const char);
556 }
557 
558 // void *(const char *fnmm, int mode)
Sc_sc_OpenFile(const RuntimeScriptValue * params,int32_t param_count)559 RuntimeScriptValue Sc_sc_OpenFile(const RuntimeScriptValue *params, int32_t param_count) {
560 	API_SCALL_OBJAUTO_POBJ_PINT(sc_File, sc_OpenFile, const char);
561 }
562 
563 // void (sc_File *fil)
Sc_File_Close(void * self,const RuntimeScriptValue * params,int32_t param_count)564 RuntimeScriptValue Sc_File_Close(void *self, const RuntimeScriptValue *params, int32_t param_count) {
565 	API_OBJCALL_VOID(sc_File, File_Close);
566 }
567 
568 // int (sc_File *fil)
Sc_File_ReadInt(void * self,const RuntimeScriptValue * params,int32_t param_count)569 RuntimeScriptValue Sc_File_ReadInt(void *self, const RuntimeScriptValue *params, int32_t param_count) {
570 	API_OBJCALL_INT(sc_File, File_ReadInt);
571 }
572 
573 // int (sc_File *fil)
Sc_File_ReadRawChar(void * self,const RuntimeScriptValue * params,int32_t param_count)574 RuntimeScriptValue Sc_File_ReadRawChar(void *self, const RuntimeScriptValue *params, int32_t param_count) {
575 	API_OBJCALL_INT(sc_File, File_ReadRawChar);
576 }
577 
578 // int (sc_File *fil)
Sc_File_ReadRawInt(void * self,const RuntimeScriptValue * params,int32_t param_count)579 RuntimeScriptValue Sc_File_ReadRawInt(void *self, const RuntimeScriptValue *params, int32_t param_count) {
580 	API_OBJCALL_INT(sc_File, File_ReadRawInt);
581 }
582 
583 // void (sc_File *fil, char* buffer)
Sc_File_ReadRawLine(void * self,const RuntimeScriptValue * params,int32_t param_count)584 RuntimeScriptValue Sc_File_ReadRawLine(void *self, const RuntimeScriptValue *params, int32_t param_count) {
585 	API_OBJCALL_VOID_POBJ(sc_File, File_ReadRawLine, char);
586 }
587 
588 // const char* (sc_File *fil)
Sc_File_ReadRawLineBack(void * self,const RuntimeScriptValue * params,int32_t param_count)589 RuntimeScriptValue Sc_File_ReadRawLineBack(void *self, const RuntimeScriptValue *params, int32_t param_count) {
590 	API_CONST_OBJCALL_OBJ(sc_File, const char, _GP(myScriptStringImpl), File_ReadRawLineBack);
591 }
592 
593 // void (sc_File *fil, char *toread)
Sc_File_ReadString(void * self,const RuntimeScriptValue * params,int32_t param_count)594 RuntimeScriptValue Sc_File_ReadString(void *self, const RuntimeScriptValue *params, int32_t param_count) {
595 	API_OBJCALL_VOID_POBJ(sc_File, File_ReadString, char);
596 }
597 
598 // const char* (sc_File *fil)
Sc_File_ReadStringBack(void * self,const RuntimeScriptValue * params,int32_t param_count)599 RuntimeScriptValue Sc_File_ReadStringBack(void *self, const RuntimeScriptValue *params, int32_t param_count) {
600 	API_CONST_OBJCALL_OBJ(sc_File, const char, _GP(myScriptStringImpl), File_ReadStringBack);
601 }
602 
603 // void (sc_File *fil, int towrite)
Sc_File_WriteInt(void * self,const RuntimeScriptValue * params,int32_t param_count)604 RuntimeScriptValue Sc_File_WriteInt(void *self, const RuntimeScriptValue *params, int32_t param_count) {
605 	API_OBJCALL_VOID_PINT(sc_File, File_WriteInt);
606 }
607 
608 // void (sc_File *fil, int towrite)
Sc_File_WriteRawChar(void * self,const RuntimeScriptValue * params,int32_t param_count)609 RuntimeScriptValue Sc_File_WriteRawChar(void *self, const RuntimeScriptValue *params, int32_t param_count) {
610 	API_OBJCALL_VOID_PINT(sc_File, File_WriteRawChar);
611 }
612 
613 // void (sc_File *fil, const char *towrite)
Sc_File_WriteRawLine(void * self,const RuntimeScriptValue * params,int32_t param_count)614 RuntimeScriptValue Sc_File_WriteRawLine(void *self, const RuntimeScriptValue *params, int32_t param_count) {
615 	API_OBJCALL_VOID_POBJ(sc_File, File_WriteRawLine, const char);
616 }
617 
618 // void (sc_File *fil, const char *towrite)
Sc_File_WriteString(void * self,const RuntimeScriptValue * params,int32_t param_count)619 RuntimeScriptValue Sc_File_WriteString(void *self, const RuntimeScriptValue *params, int32_t param_count) {
620 	API_OBJCALL_VOID_POBJ(sc_File, File_WriteString, const char);
621 }
622 
Sc_File_Seek(void * self,const RuntimeScriptValue * params,int32_t param_count)623 RuntimeScriptValue Sc_File_Seek(void *self, const RuntimeScriptValue *params, int32_t param_count) {
624 	API_OBJCALL_INT_PINT2(sc_File, File_Seek);
625 }
626 
627 // int (sc_File *fil)
Sc_File_GetEOF(void * self,const RuntimeScriptValue * params,int32_t param_count)628 RuntimeScriptValue Sc_File_GetEOF(void *self, const RuntimeScriptValue *params, int32_t param_count) {
629 	API_OBJCALL_INT(sc_File, File_GetEOF);
630 }
631 
632 // int (sc_File *fil)
Sc_File_GetError(void * self,const RuntimeScriptValue * params,int32_t param_count)633 RuntimeScriptValue Sc_File_GetError(void *self, const RuntimeScriptValue *params, int32_t param_count) {
634 	API_OBJCALL_INT(sc_File, File_GetError);
635 }
636 
Sc_File_GetPosition(void * self,const RuntimeScriptValue * params,int32_t param_count)637 RuntimeScriptValue Sc_File_GetPosition(void *self, const RuntimeScriptValue *params, int32_t param_count) {
638 	API_OBJCALL_INT(sc_File, File_GetPosition);
639 }
640 
641 
RegisterFileAPI()642 void RegisterFileAPI() {
643 	ccAddExternalStaticFunction("File::Delete^1", Sc_File_Delete);
644 	ccAddExternalStaticFunction("File::Exists^1", Sc_File_Exists);
645 	ccAddExternalStaticFunction("File::Open^2", Sc_sc_OpenFile);
646 	ccAddExternalObjectFunction("File::Close^0", Sc_File_Close);
647 	ccAddExternalObjectFunction("File::ReadInt^0", Sc_File_ReadInt);
648 	ccAddExternalObjectFunction("File::ReadRawChar^0", Sc_File_ReadRawChar);
649 	ccAddExternalObjectFunction("File::ReadRawInt^0", Sc_File_ReadRawInt);
650 	ccAddExternalObjectFunction("File::ReadRawLine^1", Sc_File_ReadRawLine);
651 	ccAddExternalObjectFunction("File::ReadRawLineBack^0", Sc_File_ReadRawLineBack);
652 	ccAddExternalObjectFunction("File::ReadString^1", Sc_File_ReadString);
653 	ccAddExternalObjectFunction("File::ReadStringBack^0", Sc_File_ReadStringBack);
654 	ccAddExternalObjectFunction("File::WriteInt^1", Sc_File_WriteInt);
655 	ccAddExternalObjectFunction("File::WriteRawChar^1", Sc_File_WriteRawChar);
656 	ccAddExternalObjectFunction("File::WriteRawLine^1", Sc_File_WriteRawLine);
657 	ccAddExternalObjectFunction("File::WriteString^1", Sc_File_WriteString);
658 	ccAddExternalObjectFunction("File::Seek^2", Sc_File_Seek);
659 	ccAddExternalObjectFunction("File::get_EOF", Sc_File_GetEOF);
660 	ccAddExternalObjectFunction("File::get_Error", Sc_File_GetError);
661 	ccAddExternalObjectFunction("File::get_Position", Sc_File_GetPosition);
662 }
663 
664 } // namespace AGS3
665