Skip to content

Commit

Permalink
ScriptAPI: implemented File.GetFileTime() and Game.GetSaveSlotTime()
Browse files Browse the repository at this point in the history
  • Loading branch information
ivan-mogilko committed Oct 4, 2024
1 parent 47ff03d commit eb472e1
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 23 deletions.
24 changes: 24 additions & 0 deletions Common/core/assetmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,30 @@ bool AssetManager::DoesAssetExist(const String &asset_name, const String &filter
return false;
}

bool AssetManager::GetAssetTime(const String &asset_name, time_t &ft, const String &filter) const
{
for (const auto *lib : _activeLibs)
{
if (!lib->TestFilter(filter)) continue; // filter does not match

if (IsAssetLibDir(lib))
{
String filename = File::FindFileCI(lib->BaseDir, asset_name);
if (!filename.IsEmpty())
{
ft = File::GetFileTime(filename);
return true;
}
}
else
{
ft = File::GetFileTime(lib->RealLibFiles[0]);
return true;
}
}
return false;
}

void AssetManager::FindAssets(std::vector<String> &assets, const String &wildcard,
const String &filter) const
{
Expand Down
3 changes: 3 additions & 0 deletions Common/core/assetmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ class AssetManager
// Tells whether asset exists in any of the registered search locations
bool DoesAssetExist(const String &asset_name, const String &filter = "") const;
inline bool DoesAssetExist(const AssetPath &apath) const { return DoesAssetExist(apath.Name, apath.Filter); }
// Tries to get asset's "file time" (last modification time).
// Note that for the assets packed within a CLIB format this will return library's time instead.
bool GetAssetTime(const String &asset_name, time_t &ft, const String &filter = "") const;
// Searches in all the registered locations and collects a list of
// assets using given wildcard pattern
// TODO: variant accepting std::regex instead of wildcard, and replace uses where convenient
Expand Down
6 changes: 6 additions & 0 deletions Common/util/file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ soff_t File::GetFileSize(const String &filename)
return size;
}

time_t File::GetFileTime(const String &filename)
{
return ags_file_time(filename.GetCStr());
// NOTE: ANDROID's AAsset storage seems to be unapplicable here
}

bool File::TestReadFile(const String &filename)
{
FILE *test_file = ags_fopen(filename.GetCStr(), "rb");
Expand Down
2 changes: 2 additions & 0 deletions Common/util/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ namespace File
bool IsFileOrDir(const String &filename);
// Returns size of a file, or -1 if no such file found
soff_t GetFileSize(const String &filename);
// Returns file's last writing time, or time_t() if no such file found
time_t GetFileTime(const String &filename);
// Tests if file could be opened for reading
bool TestReadFile(const String &filename);
// Opens a file for writing or creates new one if it does not exist; deletes file if it was created during test
Expand Down
41 changes: 30 additions & 11 deletions Common/util/stdio_compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,17 +126,17 @@ int ags_directory_exists(const char *path)

int ags_path_exists(const char *path)
{
#if AGS_PLATFORM_OS_WINDOWS
WCHAR wstr[MAX_PATH_SZ];
MultiByteToWideChar(CP_UTF8, 0, path, -1, wstr, MAX_PATH_SZ);
return PathFileExistsW(wstr);
#else
struct stat path_stat;
if (stat(path, &path_stat) != 0) {
return 0;
}
return S_ISREG(path_stat.st_mode) || S_ISDIR(path_stat.st_mode);
#endif
#if AGS_PLATFORM_OS_WINDOWS
WCHAR wstr[MAX_PATH_SZ];
MultiByteToWideChar(CP_UTF8, 0, path, -1, wstr, MAX_PATH_SZ);
return PathFileExistsW(wstr);
#else
struct stat path_stat;
if (stat(path, &path_stat) != 0) {
return 0;
}
return S_ISREG(path_stat.st_mode) || S_ISDIR(path_stat.st_mode);
#endif
}

file_off_t ags_file_size(const char *path)
Expand All @@ -158,6 +158,25 @@ file_off_t ags_file_size(const char *path)
#endif
}

time_t ags_file_time(const char *path)
{
#if AGS_PLATFORM_OS_WINDOWS
WCHAR wstr[MAX_PATH_SZ];
MultiByteToWideChar(CP_UTF8, 0, path, -1, wstr, MAX_PATH_SZ);
struct _stat64 path_stat;
if (_wstat64(wstr, &path_stat) != 0) {
return -1;
}
return path_stat.st_mtime;
#else
struct stat path_stat;
if (stat(path, &path_stat) != 0) {
return -1;
}
return path_stat.st_mtime;
#endif
}

int ags_file_remove(const char *path)
{
#if AGS_PLATFORM_OS_WINDOWS
Expand Down
2 changes: 2 additions & 0 deletions Common/util/stdio_compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include <stdio.h>
#include <stdint.h>
#include <time.h>

typedef int64_t file_off_t;

Expand All @@ -38,6 +39,7 @@ int ags_file_exists(const char *path);
int ags_directory_exists(const char *path);
int ags_path_exists(const char *path);
file_off_t ags_file_size(const char *path);
time_t ags_file_time(const char *path);

int ags_file_remove(const char *path);
int ags_file_rename(const char *src, const char *dst);
Expand Down
10 changes: 10 additions & 0 deletions Editor/AGS.Editor/Resources/agsdefns.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1278,6 +1278,8 @@ enum FileSeek {
};
#endif

builtin managed struct DateTime;

builtin managed struct File {
/// Delets the specified file from the disk.
import static bool Delete(const string filename); // $AUTOCOMPLETESTATICONLY$
Expand Down Expand Up @@ -1328,6 +1330,10 @@ builtin managed struct File {
import static String ResolvePath(const string filename); // $AUTOCOMPLETESTATICONLY$
/// Gets the path to opened file.
readonly import attribute String Path;
#endif
#ifdef SCRIPT_API_v362
/// Retrieves specified file's last write time; returns null if file does not exist
import static DateTime* GetFileTime(const string filename); // $AUTOCOMPLETESTATICONLY$
#endif
int reserved[2]; // $AUTOCOMPLETEIGNORE$
};
Expand Down Expand Up @@ -3045,6 +3051,10 @@ builtin struct Game {
/// Preloads and caches sprites and linked sounds for a view, within a selected range of loops.
import static void PrecacheView(int view, int first_loop, int last_loop);
#endif
#ifdef SCRIPT_API_v362
/// Gets the write time of the specified save game slot.
import static DateTime* GetSaveSlotTime(int saveSlot);
#endif
};

builtin struct GameState {
Expand Down
14 changes: 14 additions & 0 deletions Engine/ac/dynobj/scriptdatetime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ const char *ScriptDateTime::GetType() {
return "DateTime";
}

void ScriptDateTime::SetFromStdTime(time_t time)
{
// NOTE: subject to year 2038 problem due to shoving time_t in an integer
rawUnixTime = static_cast<int>(time);

struct tm *newtime = localtime(&time);
hour = newtime->tm_hour;
minute = newtime->tm_min;
second = newtime->tm_sec;
day = newtime->tm_mday;
month = newtime->tm_mon + 1;
year = newtime->tm_year + 1900;
}

size_t ScriptDateTime::CalcSerializeSize(const void* /*address*/)
{
return sizeof(int32_t) * 7;
Expand Down
3 changes: 3 additions & 0 deletions Engine/ac/dynobj/scriptdatetime.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#ifndef __AGS_EE_DYNOBJ__SCRIPTDATETIME_H
#define __AGS_EE_DYNOBJ__SCRIPTDATETIME_H

#include <time.h>
#include "ac/dynobj/cc_agsdynamicobject.h"

struct ScriptDateTime final : AGSCCDynamicObject {
Expand All @@ -29,6 +30,8 @@ struct ScriptDateTime final : AGSCCDynamicObject {
const char *GetType() override;
void Unserialize(int index, AGS::Common::Stream *in, size_t data_sz) override;

void SetFromStdTime(time_t time);

ScriptDateTime();

protected:
Expand Down
28 changes: 28 additions & 0 deletions Engine/ac/file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,28 @@ int File_Exists(const char *fnmm) {
return 1; // was found in fs
}

ScriptDateTime* File_GetFileTime(const char *fnmm) {
const auto rp = ResolveScriptPathAndFindFile(fnmm, true);
if (!rp)
return nullptr;

time_t ft;
if (rp.AssetMgr)
{
if (!AssetMgr->GetAssetTime(rp.FullPath, ft, "*"))
return nullptr;
}
else
{
ft = File::GetFileTime(rp.FullPath);
}

ScriptDateTime *sdt = new ScriptDateTime();
sdt->SetFromStdTime(ft);
ccRegisterManagedObject(sdt, sdt);
return sdt;
}

int File_Delete(const char *fnmm) {
const auto rp = ResolveScriptPathAndFindFile(fnmm, false);
if (!rp)
Expand Down Expand Up @@ -763,6 +785,11 @@ RuntimeScriptValue Sc_File_Exists(const RuntimeScriptValue *params, int32_t para
API_SCALL_INT_POBJ(File_Exists, const char);
}

RuntimeScriptValue Sc_File_GetFileTime(const RuntimeScriptValue *params, int32_t param_count)
{
API_SCALL_OBJAUTO_POBJ(ScriptDateTime, File_GetFileTime, const char);
}

// void *(const char *fnmm, int mode)
RuntimeScriptValue Sc_sc_OpenFile(const RuntimeScriptValue *params, int32_t param_count)
{
Expand Down Expand Up @@ -884,6 +911,7 @@ void RegisterFileAPI()
ScFnRegister file_api[] = {
{ "File::Delete^1", API_FN_PAIR(File_Delete) },
{ "File::Exists^1", API_FN_PAIR(File_Exists) },
{ "File::GetFileTime^1", API_FN_PAIR(File_GetFileTime) },
{ "File::Open^2", API_FN_PAIR(sc_OpenFile) },
{ "File::ResolvePath^1", API_FN_PAIR(File_ResolvePath) },

Expand Down
15 changes: 15 additions & 0 deletions Engine/ac/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,15 @@ const char* Game_GetSaveSlotDescription(int slnum) {
return nullptr;
}

ScriptDateTime* Game_GetSaveSlotTime(int slnum)
{
time_t ft = File::GetFileTime(get_save_game_path(slnum));
ScriptDateTime *sdt = new ScriptDateTime();
sdt->SetFromStdTime(ft);
ccRegisterManagedObject(sdt, sdt);
return sdt;
}

void restore_game_dialog() {
restore_game_dialog2(1, LEGACY_TOP_BUILTINDIALOGSAVESLOT);
}
Expand Down Expand Up @@ -1608,6 +1617,11 @@ RuntimeScriptValue Sc_Game_GetSaveSlotDescription(const RuntimeScriptValue *para
API_SCALL_OBJ_PINT(const char, myScriptStringImpl, Game_GetSaveSlotDescription);
}

RuntimeScriptValue Sc_Game_GetSaveSlotTime(const RuntimeScriptValue *params, int32_t param_count)
{
API_SCALL_OBJAUTO_PINT(ScriptDateTime, Game_GetSaveSlotTime);
}

// ScriptViewFrame* (int viewNumber, int loopNumber, int frame)
RuntimeScriptValue Sc_Game_GetViewFrame(const RuntimeScriptValue *params, int32_t param_count)
{
Expand Down Expand Up @@ -1882,6 +1896,7 @@ void RegisterGameAPI()
{ "Game::GetMODPattern^0", API_FN_PAIR(Game_GetMODPattern) },
{ "Game::GetRunNextSettingForLoop^2", API_FN_PAIR(Game_GetRunNextSettingForLoop) },
{ "Game::GetSaveSlotDescription^1", API_FN_PAIR(Game_GetSaveSlotDescription) },
{ "Game::GetSaveSlotTime^1", API_FN_PAIR(Game_GetSaveSlotTime) },
{ "Game::GetViewFrame^3", API_FN_PAIR(Game_GetViewFrame) },
{ "Game::InputBox^1", API_FN_PAIR(Game_InputBox) },
{ "Game::SetSaveGameDirectory^1", API_FN_PAIR(Game_SetSaveGameDirectory) },
Expand Down
15 changes: 3 additions & 12 deletions Engine/platform/base/agsplatformdriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,19 +78,10 @@ const char *AGSPlatformDriver::GetDiskWriteAccessTroubleshootingText()
return "Make sure you have write permissions, and also check the disk's free space.";
}

void AGSPlatformDriver::GetSystemTime(ScriptDateTime *sdt) {
void AGSPlatformDriver::GetSystemTime(ScriptDateTime *sdt)
{
time_t t = time(nullptr);

//note: subject to year 2038 problem due to shoving time_t in an integer
sdt->rawUnixTime = static_cast<int>(t);

struct tm *newtime = localtime(&t);
sdt->hour = newtime->tm_hour;
sdt->minute = newtime->tm_min;
sdt->second = newtime->tm_sec;
sdt->day = newtime->tm_mday;
sdt->month = newtime->tm_mon + 1;
sdt->year = newtime->tm_year + 1900;
sdt->SetFromStdTime(t);
}

void AGSPlatformDriver::DisplayAlert(const char *text, ...)
Expand Down

0 comments on commit eb472e1

Please sign in to comment.