mirror of
				https://github.com/coop-deluxe/sm64coopdx.git
				synced 2025-10-30 08:01:01 +00:00 
			
		
		
		
	Regenerate DynOS assets when source files are modified (#873)
	
		
			
	
		
	
	
		
	
		
			Some checks are pending
		
		
	
	
		
			
				
	
				Build coop / build-linux (push) Waiting to run
				
			
		
			
				
	
				Build coop / build-steamos (push) Waiting to run
				
			
		
			
				
	
				Build coop / build-windows-opengl (push) Waiting to run
				
			
		
			
				
	
				Build coop / build-windows-directx (push) Waiting to run
				
			
		
			
				
	
				Build coop / build-macos-arm (push) Waiting to run
				
			
		
			
				
	
				Build coop / build-macos-intel (push) Waiting to run
				
			
		
		
	
	
				
					
				
			
		
			Some checks are pending
		
		
	
	Build coop / build-linux (push) Waiting to run
				
			Build coop / build-steamos (push) Waiting to run
				
			Build coop / build-windows-opengl (push) Waiting to run
				
			Build coop / build-windows-directx (push) Waiting to run
				
			Build coop / build-macos-arm (push) Waiting to run
				
			Build coop / build-macos-intel (push) Waiting to run
				
			Previously to recompile DynOS assets you had to remove the bin/lvl/bhv/tex/col/etc files. Now the game will compare the last-modified-timestamps of the generated/compiled assets vs the other files that are within those directories. If the presumed- source files have a later modified timestamp DynOS will regenerate those assets. While this results in scanning the attributes of files more, it also prevents parsing files unnecessarily. Previously actors would always parse their source files and build up GfxData unnecessarily. --------- Co-authored-by: MysterD <myster@d>
This commit is contained in:
		
							parent
							
								
									6a5af9d23a
								
							
						
					
					
						commit
						81af37eef6
					
				
					 13 changed files with 252 additions and 97 deletions
				
			
		|  | @ -1019,6 +1019,12 @@ s64 DynOS_RecursiveDescent_Parse(const char* expr, bool* success, RDConstantFunc | |||
| void DynOS_Read_Source(GfxData *aGfxData, const SysPath &aFilename); | ||||
| char *DynOS_Read_Buffer(FILE* aFile, GfxData* aGfxData); | ||||
| 
 | ||||
| bool DynOS_ShouldGeneratePack(const SysPath &aPackFolder, std::initializer_list<const char*> aExtensions); | ||||
| bool DynOS_ShouldGeneratePack2Ext(const SysPath &aPackFolder, const char *aGenExtension, const char *aSrcExtension); | ||||
| bool DynOS_GenFileExistsAndIsNewerThanFile(const SysPath &aGenFile, const SysPath &aSrcFile); | ||||
| bool DynOS_GenFileExistsAndIsNewerThanFolder(const SysPath &aGenFile, const SysPath &aSrcFolder); | ||||
| String DynOS_GetActorFolder(const Array<Pair<u64, String>> &aActorsFolders, u64 aModelIdentifier); | ||||
| 
 | ||||
| s64 DynOS_Misc_ParseInteger(const String& _Arg, bool* found); | ||||
| 
 | ||||
| void DynOS_Anim_ScanFolder(GfxData *aGfxData, const SysPath &aAnimsFolder); | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ | |||
| #include <utility> | ||||
| #include <string> | ||||
| #include <map> | ||||
| #include <initializer_list> | ||||
| extern "C" { | ||||
| #endif | ||||
| #include "config.h" | ||||
|  |  | |||
|  | @ -131,33 +131,8 @@ GfxData *DynOS_Actor_LoadFromBinary(const SysPath &aPackFolder, const char *aAct | |||
|  // Generate //
 | ||||
| //////////////
 | ||||
| 
 | ||||
| static String GetActorFolder(const Array<Pair<u64, String>> &aActorsFolders, u64 aModelIdentifier) { | ||||
|     for (const auto &_Pair : aActorsFolders) { | ||||
|         if (_Pair.first == aModelIdentifier) { | ||||
|             return _Pair.second; | ||||
|         } | ||||
|     } | ||||
|     return String(); | ||||
| } | ||||
| 
 | ||||
| static void DynOS_Actor_Generate(const SysPath &aPackFolder, Array<Pair<u64, String>> _ActorsFolders, GfxData *_GfxData) { | ||||
|     // do not regen this folder if we find any existing bins
 | ||||
|     for (s32 geoIndex = _GfxData->mGeoLayouts.Count() - 1; geoIndex >= 0; geoIndex--) { | ||||
|         auto &_GeoNode = _GfxData->mGeoLayouts[geoIndex]; | ||||
|         String _GeoRootName = _GeoNode->mName; | ||||
| 
 | ||||
|         // If there is an existing binary file for this layout, skip and go to the next actor
 | ||||
|         SysPath _BinFilename = fstring("%s/%s.bin", aPackFolder.c_str(), _GeoRootName.begin()); | ||||
|         if (fs_sys_file_exists(_BinFilename.c_str())) { | ||||
| 
 | ||||
|             // Compress file to gain some space
 | ||||
|             if (configCompressOnStartup && !DynOS_Bin_IsCompressed(_BinFilename)) { | ||||
|                 DynOS_Bin_Compress(_BinFilename); | ||||
|             } | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|     Array<String> _SkipActorFolders; | ||||
| 
 | ||||
|     // generate in reverse order to detect children
 | ||||
|     for (s32 geoIndex = _GfxData->mGeoLayouts.Count() - 1; geoIndex >= 0; geoIndex--) { | ||||
|  | @ -173,6 +148,18 @@ static void DynOS_Actor_Generate(const SysPath &aPackFolder, Array<Pair<u64, Str | |||
|         // If there is an existing binary file for this layout, skip and go to the next actor
 | ||||
|         SysPath _BinFilename = fstring("%s/%s.bin", aPackFolder.c_str(), _GeoRootName.begin()); | ||||
| 
 | ||||
|         // If there is an existing binary file for this actor, skip and go to the next actor
 | ||||
|         String _ActorFolder = DynOS_GetActorFolder(_ActorsFolders, _GeoNode->mModelIdentifier); | ||||
|         SysPath _SrcFolder = fstring("%s/%s", aPackFolder.c_str(), _ActorFolder.begin()); | ||||
|         if (DynOS_GenFileExistsAndIsNewerThanFolder(_BinFilename, _SrcFolder)) { | ||||
|             // Remember that we skipped this folder, so we can skip it again in the future.
 | ||||
|             // This prevents generating child geo bins when we shouldn't.
 | ||||
|             _SkipActorFolders.Add(_ActorFolder); | ||||
|             continue; | ||||
|         } else if (_SkipActorFolders.Find(_ActorFolder) != -1) { | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         // Init
 | ||||
|         _GfxData->mLoadIndex                  = 0; | ||||
|         _GfxData->mErrorCount                 = 0; | ||||
|  | @ -201,7 +188,6 @@ static void DynOS_Actor_Generate(const SysPath &aPackFolder, Array<Pair<u64, Str | |||
|         _GfxData->mAnimationTable.Clear(); | ||||
| 
 | ||||
|         // Scan anims folder for animation data
 | ||||
|         String _ActorFolder = GetActorFolder(_ActorsFolders, _GfxData->mModelIdentifier); | ||||
|         SysPath _AnimsFolder = fstring("%s/%s/anims", aPackFolder.c_str(), _ActorFolder.begin()); | ||||
|         DynOS_Anim_ScanFolder(_GfxData, _AnimsFolder); | ||||
| 
 | ||||
|  | @ -246,6 +232,11 @@ static void DynOS_Actor_Generate(const SysPath &aPackFolder, Array<Pair<u64, Str | |||
| 
 | ||||
| void DynOS_Actor_GeneratePack(const SysPath &aPackFolder) { | ||||
|     Print("Processing actors: \"%s\"", aPackFolder.c_str()); | ||||
| 
 | ||||
|     if (!DynOS_ShouldGeneratePack(aPackFolder,  { ".bin", ".col" })) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     Array<Pair<u64, String>> _ActorsFolders; | ||||
|     GfxData *_GfxData = New<GfxData>(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -2602,18 +2602,6 @@ static String GetBehaviorFolder(const Array<Pair<u64, String>> &aBehaviorsFolder | |||
| } | ||||
| 
 | ||||
| static void DynOS_Bhv_Generate(const SysPath &aPackFolder, Array<Pair<u64, String>> _BehaviorsFolders, GfxData *_GfxData) { | ||||
|     // do not regen this folder if we find any existing bins
 | ||||
|     for (s32 bhvIndex = _GfxData->mBehaviorScripts.Count() - 1; bhvIndex >= 0; bhvIndex--) { | ||||
|         auto &_BhvNode = _GfxData->mBehaviorScripts[bhvIndex]; | ||||
|         String _BhvRootName = _BhvNode->mName; | ||||
| 
 | ||||
|         // If there is an existing binary file for this layout, skip and go to the next behavior.
 | ||||
|         SysPath _BinFilename = fstring("%s/%s.bhv", aPackFolder.c_str(), _BhvRootName.begin()); | ||||
|         if (fs_sys_file_exists(_BinFilename.c_str())) { | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // generate in reverse order to detect children
 | ||||
|     for (s32 bhvIndex = _GfxData->mBehaviorScripts.Count() - 1; bhvIndex >= 0; bhvIndex--) { | ||||
|         auto &_BhvNode = _GfxData->mBehaviorScripts[bhvIndex]; | ||||
|  | @ -2660,6 +2648,11 @@ static void DynOS_Bhv_Generate(const SysPath &aPackFolder, Array<Pair<u64, Strin | |||
| 
 | ||||
| void DynOS_Bhv_GeneratePack(const SysPath &aPackFolder) { | ||||
|     Print("Processing behaviors: \"%s\"", aPackFolder.c_str()); | ||||
| 
 | ||||
|     if (!DynOS_ShouldGeneratePack2Ext(aPackFolder, ".bhv", ".c")) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     Array<Pair<u64, String>> _BehaviorsFolders; | ||||
|     GfxData *_GfxData = New<GfxData>(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -687,16 +687,12 @@ DataNode<Collision>* DynOS_Col_LoadFromBinary(const SysPath &aFilename, const ch | |||
| void DynOS_Col_Generate(const SysPath &aPackFolder, Array<Pair<u64, String>> _ActorsFolders, GfxData *_GfxData) { | ||||
|     for (auto &_ColNode : _GfxData->mCollisions) { | ||||
|         String _ColRootName = _ColNode->mName; | ||||
| 
 | ||||
|         // If there is an existing binary file for this collision, skip and go to the next actor
 | ||||
|         SysPath _ColFilename = fstring("%s/%s.col", aPackFolder.c_str(), _ColRootName.begin()); | ||||
|         if (fs_sys_file_exists(_ColFilename.c_str())) { | ||||
| 
 | ||||
|             // Compress file to gain some space
 | ||||
|             if (configCompressOnStartup && !DynOS_Bin_IsCompressed(_ColFilename)) { | ||||
|                 DynOS_Bin_Compress(_ColFilename); | ||||
|             } | ||||
| 
 | ||||
|         // If there is an existing binary file for this collision, skip and go to the next collision
 | ||||
|         String _ActorFolder = DynOS_GetActorFolder(_ActorsFolders, _ColNode->mModelIdentifier); | ||||
|         SysPath _SrcFilename = fstring("%s/%s/collision.inc.c", aPackFolder.c_str(), _ActorFolder.begin()); | ||||
|         if (DynOS_GenFileExistsAndIsNewerThanFile(_ColFilename, _SrcFilename)) { | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1126,15 +1126,6 @@ static bool DynOS_Lvl_GeneratePack_Internal(const SysPath &aPackFolder, Array<Pa | |||
|         if (_LvlRootName.Find("_entry") == -1) { continue; } | ||||
|         // If there is an existing binary file for this level, skip and go to the next level
 | ||||
|         SysPath _LvlFilename = fstring("%s/%s.lvl", aPackFolder.c_str(), _LvlRootName.begin()); | ||||
|         if (fs_sys_file_exists(_LvlFilename.c_str())) { | ||||
| 
 | ||||
|             // Compress file to gain some space
 | ||||
|             if (configCompressOnStartup && !DynOS_Bin_IsCompressed(_LvlFilename)) { | ||||
|                 DynOS_Bin_Compress(_LvlFilename); | ||||
|             } | ||||
| 
 | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         // Init
 | ||||
|         _GfxData->mLoadIndex                  = 0; | ||||
|  | @ -1241,6 +1232,11 @@ static void DynOS_Lvl_GeneratePack_Recursive(const SysPath &directory, GfxData * | |||
| 
 | ||||
| void DynOS_Lvl_GeneratePack(const SysPath &aPackFolder) { | ||||
|     Print("Processing levels: \"%s\"", aPackFolder.c_str()); | ||||
| 
 | ||||
|     if (!DynOS_ShouldGeneratePack(aPackFolder, { ".lvl" })) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     Array<Pair<u64, String>> _ActorsFolders; | ||||
| 
 | ||||
|     GfxData *_GfxData = New<GfxData>(); | ||||
|  | @ -1256,8 +1252,9 @@ void DynOS_Lvl_GeneratePack(const SysPath &aPackFolder) { | |||
|             if (SysPath(_PackEnt->d_name) == "..") continue; | ||||
| 
 | ||||
|             // Compress .lvl files to gain some space
 | ||||
|             bool _IsLvl = (SysPath(_PackEnt->d_name).find(".lvl") != SysPath::npos); | ||||
|             SysPath _Filename = fstring("%s/%s", aPackFolder.c_str(), _PackEnt->d_name); | ||||
|             if (SysPath(_PackEnt->d_name).find(".lvl") != SysPath::npos && !DynOS_Bin_IsCompressed(_Filename)) { | ||||
|             if (_IsLvl && !DynOS_Bin_IsCompressed(_Filename)) { | ||||
|                 if (configCompressOnStartup) { DynOS_Bin_Compress(_Filename); } | ||||
|                 continue; | ||||
|             } | ||||
|  | @ -1266,12 +1263,20 @@ void DynOS_Lvl_GeneratePack(const SysPath &aPackFolder) { | |||
|             SysPath _Folder = fstring("%s/%s", aPackFolder.c_str(), _PackEnt->d_name); | ||||
|             if (!fs_sys_dir_exists(_Folder.c_str())) continue; | ||||
| 
 | ||||
|             // Only parse folders with a 'script.c'
 | ||||
|             SysPath _ScriptFile = fstring("%s/script.c", _Folder.c_str()); | ||||
|             if (!fs_sys_file_exists(_ScriptFile.c_str())) { | ||||
|                 _ScriptFile = fstring("%s/custom.script.c", _Folder.c_str()); | ||||
|                 if (!fs_sys_file_exists(_ScriptFile.c_str())) { | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             // Prevent generating from folders that likely already generated
 | ||||
|             SysPath _LvlFile = fstring("%s/level_%s_entry.lvl", aPackFolder.c_str(), _PackEnt->d_name); | ||||
|             if (fs_sys_file_exists(_LvlFile.c_str())) continue; | ||||
| 
 | ||||
|             // Only parse folders with a 'script.c'
 | ||||
|             if (!fs_sys_file_exists(fstring("%s/script.c", _Folder.c_str()).c_str()) && !fs_sys_file_exists(fstring("%s/custom.script.c", _Folder.c_str()).c_str())) continue; | ||||
|             if (DynOS_GenFileExistsAndIsNewerThanFolder(_LvlFile, _Folder)) { | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             _GfxData->mModelIdentifier++; | ||||
|             DynOS_Lvl_GeneratePack_Recursive(_Folder, _GfxData); | ||||
|  |  | |||
|  | @ -409,7 +409,6 @@ static void DynOS_Tex_GeneratePack_Recursive(const SysPath &aPackFolder, SysPath | |||
|             continue; | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         size_t nameLen = strlen(_PackEnt->d_name); | ||||
|         if (nameLen < 4) continue; | ||||
| 
 | ||||
|  | @ -418,13 +417,6 @@ static void DynOS_Tex_GeneratePack_Recursive(const SysPath &aPackFolder, SysPath | |||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         // skip files that have already been generated
 | ||||
|         char buffer[SYS_MAX_PATH]; | ||||
|         snprintf(buffer, SYS_MAX_PATH, "%s.tex", _Path.substr(0, _Path.size() - 4).c_str()); | ||||
|         if (fs_sys_file_exists(buffer)) { | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         // read the file
 | ||||
|         aGfxData->mModelIdentifier++; | ||||
|         TexData* _TexData = LoadTextureFromFile(aGfxData, _Path.c_str()); | ||||
|  | @ -460,6 +452,12 @@ static void DynOS_Tex_GeneratePack_Recursive(const SysPath &aPackFolder, SysPath | |||
| 
 | ||||
|         SysPath _OutputPath = fstring("%s/%s.tex", aOutputFolder.c_str(), _BaseName.begin()); | ||||
| 
 | ||||
|         // skip files that have already been generated
 | ||||
|         if (DynOS_GenFileExistsAndIsNewerThanFile(_OutputPath, _Path)) { | ||||
|             Delete<TexData>(_TexData); | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         // create output dir if it doesn't exist
 | ||||
|         if (!fs_sys_dir_exists(aOutputFolder.c_str())) { | ||||
|             fs_sys_mkdir(aOutputFolder.c_str()); | ||||
|  | @ -477,6 +475,10 @@ static void DynOS_Tex_GeneratePack_Recursive(const SysPath &aPackFolder, SysPath | |||
| void DynOS_Tex_GeneratePack(const SysPath &aPackFolder, SysPath &aOutputFolder, bool aAllowCustomTextures) { | ||||
|     Print("Processing textures: \"%s\"", aPackFolder.c_str()); | ||||
| 
 | ||||
|     if (!DynOS_ShouldGeneratePack2Ext(aPackFolder, ".tex", ".png")) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     GfxData *_GfxData = New<GfxData>(); | ||||
|     _GfxData->mModelIdentifier = 0; | ||||
|     SysPath _Empty = ""; | ||||
|  |  | |||
|  | @ -1,5 +1,166 @@ | |||
| #include "dynos.cpp.h" | ||||
| 
 | ||||
|   ////////////////////////////////
 | ||||
|  // Should-generate-pack logic //
 | ||||
| ////////////////////////////////
 | ||||
| 
 | ||||
| static bool DynOS_PathHasExtension(const char *aPath, const char *aExtension) { | ||||
|     size_t _LenStr    = strlen(aPath); | ||||
|     size_t _LenSuffix = strlen(aExtension); | ||||
| 
 | ||||
|     if (_LenSuffix > _LenStr) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return strcmp(aPath + (_LenStr - _LenSuffix), aExtension) == 0; | ||||
| } | ||||
| 
 | ||||
| static bool DynOS_PathHasExtensions(const char *aPath, std::initializer_list<const char*> aExtensions) { | ||||
|     for (auto _Ext : aExtensions) { | ||||
|         if (DynOS_PathHasExtension(aPath, _Ext)) { | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| static void DynOS_GetMTimeInFolderSplitByExtensions(const SysPath &aPath, std::initializer_list<const char*> aExtensions, u64 *aLatestMTimeExt, u64 *aLatestMTimeNonExt) { | ||||
|     DIR *_DirPath = opendir(aPath.c_str()); | ||||
|     if (_DirPath) { | ||||
|         struct dirent *_DirEnt = NULL; | ||||
|         while ((_DirEnt = readdir(_DirPath)) != NULL) { | ||||
| 
 | ||||
|             // Skip . and ..
 | ||||
|             if (SysPath(_DirEnt->d_name) == ".")  { continue; } | ||||
|             if (SysPath(_DirEnt->d_name) == "..") { continue; } | ||||
| 
 | ||||
|             SysPath _Path = fstring("%s/%s", aPath.c_str(), _DirEnt->d_name); | ||||
| 
 | ||||
|             // Recursively accumulate maximum mtimes
 | ||||
|             if (fs_sys_dir_exists(_Path.c_str())) { | ||||
|                 DynOS_GetMTimeInFolderSplitByExtensions(_Path, aExtensions, aLatestMTimeExt, aLatestMTimeNonExt); | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             // Accumulate max mtime in the correct slot
 | ||||
|             u64 _PathMTime = fs_sys_get_modified_time(_Path.c_str()); | ||||
|             if (DynOS_PathHasExtensions(_Path.c_str(), aExtensions)) { | ||||
|                 *aLatestMTimeExt = MAX(*aLatestMTimeExt, _PathMTime); | ||||
|             } else { | ||||
|                 *aLatestMTimeNonExt = MAX(*aLatestMTimeNonExt, _PathMTime); | ||||
|             } | ||||
|         } | ||||
|         closedir(_DirPath); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void DynOS_GetMTimeInFolderSplitBy2Extensions(const SysPath &aPath, const char * aExt1, const char *aExt2, u64 *aLatestMTimeExt1, u64 *aLatestMTimeExt2) { | ||||
|     DIR *_DirPath = opendir(aPath.c_str()); | ||||
|     if (_DirPath) { | ||||
|         struct dirent *_DirEnt = NULL; | ||||
|         while ((_DirEnt = readdir(_DirPath)) != NULL) { | ||||
| 
 | ||||
|             // Skip . and ..
 | ||||
|             if (SysPath(_DirEnt->d_name) == ".")  { continue; } | ||||
|             if (SysPath(_DirEnt->d_name) == "..") { continue; } | ||||
| 
 | ||||
|             SysPath _Path = fstring("%s/%s", aPath.c_str(), _DirEnt->d_name); | ||||
| 
 | ||||
|             // Recursively accumulate maximum mtimes
 | ||||
|             if (fs_sys_dir_exists(_Path.c_str())) { | ||||
|                 DynOS_GetMTimeInFolderSplitBy2Extensions(_Path, aExt1, aExt2, aLatestMTimeExt1, aLatestMTimeExt2); | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             // Accumulate max mtime in the correct slot
 | ||||
|             u64 _PathMTime = fs_sys_get_modified_time(_Path.c_str()); | ||||
|             if (DynOS_PathHasExtension(_Path.c_str(), aExt1)) { | ||||
|                 *aLatestMTimeExt1 = MAX(*aLatestMTimeExt1, _PathMTime); | ||||
|             } else if (DynOS_PathHasExtension(_Path.c_str(), aExt2)) { | ||||
|                 *aLatestMTimeExt2 = MAX(*aLatestMTimeExt2, _PathMTime); | ||||
|             } | ||||
|         } | ||||
|         closedir(_DirPath); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static u64 DynOS_GetMTimeInFolder(const SysPath &aPath) { | ||||
|     u64 _LatestMTimeSubDir = 0; | ||||
| 
 | ||||
|     DIR *_DirPath = opendir(aPath.c_str()); | ||||
|     if (_DirPath) { | ||||
|         struct dirent *_DirEnt = NULL; | ||||
|         while ((_DirEnt = readdir(_DirPath)) != NULL) { | ||||
| 
 | ||||
|             // Skip . and ..
 | ||||
|             if (SysPath(_DirEnt->d_name) == ".")  { continue; } | ||||
|             if (SysPath(_DirEnt->d_name) == "..") { continue; } | ||||
| 
 | ||||
|             // Check get the mtime of the file and store the max mtime
 | ||||
|             SysPath _Path = fstring("%s/%s", aPath.c_str(), _DirEnt->d_name); | ||||
|             if (fs_sys_dir_exists(_Path.c_str())) { | ||||
|                 u64 _PathMTime = DynOS_GetMTimeInFolder(_Path); | ||||
|                 _LatestMTimeSubDir = MAX(_LatestMTimeSubDir, _PathMTime); | ||||
|             } else { | ||||
|                 u64 _PathMTime = fs_sys_get_modified_time(_Path.c_str()); | ||||
|                 _LatestMTimeSubDir = MAX(_LatestMTimeSubDir, _PathMTime); | ||||
|             } | ||||
|         } | ||||
|         closedir(_DirPath); | ||||
|     } | ||||
| 
 | ||||
|     return _LatestMTimeSubDir; | ||||
| } | ||||
| 
 | ||||
| bool DynOS_ShouldGeneratePack(const SysPath &aPackFolder, std::initializer_list<const char*> aExtensions) { | ||||
|     u64 _LatestMTimeExt = 0; | ||||
|     u64 _LatestMTimeNonExt = 0; | ||||
| 
 | ||||
|     DynOS_GetMTimeInFolderSplitByExtensions(aPackFolder, aExtensions, &_LatestMTimeExt, &_LatestMTimeNonExt); | ||||
| 
 | ||||
|     return _LatestMTimeExt < _LatestMTimeNonExt; | ||||
| } | ||||
| 
 | ||||
| bool DynOS_ShouldGeneratePack2Ext(const SysPath &aPackFolder, const char *aGenExtension, const char *aSrcExtension) { | ||||
|     u64 _LatestMTimeGenExt = 0; | ||||
|     u64 _LatestMTimeSrcExt = 0; | ||||
| 
 | ||||
|     DynOS_GetMTimeInFolderSplitBy2Extensions(aPackFolder, aGenExtension, aSrcExtension, &_LatestMTimeGenExt, &_LatestMTimeSrcExt); | ||||
| 
 | ||||
|     return _LatestMTimeGenExt < _LatestMTimeSrcExt; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| bool DynOS_GenFileExistsAndIsNewerThanFile(const SysPath &aGenFile, const SysPath &aSrcFile) { | ||||
|     if (fs_sys_file_exists(aGenFile.c_str())) { | ||||
|         // compare modified times
 | ||||
|         u64 _MTimeGenFile    = fs_sys_get_modified_time(aGenFile.c_str()); | ||||
|         u64 _MTimeSourceFile = fs_sys_get_modified_time(aSrcFile.c_str()); | ||||
|         return (_MTimeGenFile >= _MTimeSourceFile); | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool DynOS_GenFileExistsAndIsNewerThanFolder(const SysPath &aGenFile, const SysPath &aSrcFolder) { | ||||
|     if (fs_sys_file_exists(aGenFile.c_str())) { | ||||
|         // compare modified times
 | ||||
|         u64 _MTimeGenFile      = fs_sys_get_modified_time(aGenFile.c_str()); | ||||
|         u64 _MTimeSourceFolder = DynOS_GetMTimeInFolder(aSrcFolder); | ||||
|         return (_MTimeGenFile >= _MTimeSourceFolder); | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| String DynOS_GetActorFolder(const Array<Pair<u64, String>> &aActorsFolders, u64 aModelIdentifier) { | ||||
|     for (const auto &_Pair : aActorsFolders) { | ||||
|         if (_Pair.first == aModelIdentifier) { | ||||
|             return _Pair.second; | ||||
|         } | ||||
|     } | ||||
|     return String(); | ||||
| } | ||||
| 
 | ||||
|   //////////
 | ||||
|  // Misc //
 | ||||
| //////////
 | ||||
|  |  | |||
|  | @ -346,6 +346,31 @@ bool fs_sys_dir_is_empty(const char *name) { | |||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| uint64_t fs_sys_get_modified_time(const char *path) { | ||||
| #ifdef _WIN32 | ||||
|     WIN32_FILE_ATTRIBUTE_DATA fad; | ||||
|     // get attributes, return 0 on error
 | ||||
|     if (!GetFileAttributesExA(path, GetFileExInfoStandard, &fad)) { return 0; } | ||||
| 
 | ||||
|     // filetime is 100-ns intervals since 1601-01-01 UTC
 | ||||
|     ULARGE_INTEGER ull; | ||||
|     ull.LowPart  = fad.ftLastWriteTime.dwLowDateTime; | ||||
|     ull.HighPart = fad.ftLastWriteTime.dwHighDateTime; | ||||
| 
 | ||||
|     // 100-ns from 1601 to 1970
 | ||||
|     const uint64_t EPOCH_DIFF = 116444736000000000ULL; | ||||
|     uint64_t time100ns = ull.QuadPart; | ||||
| 
 | ||||
|     // convert to seconds
 | ||||
|     return (time100ns - EPOCH_DIFF) / 10000000ULL; | ||||
| #else | ||||
|     struct stat st; | ||||
|     // get stat, return 0 on error
 | ||||
|     if (stat(path, &st) != 0) { return 0; } | ||||
|     return (uint64_t)st.st_mtime; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| bool fs_sys_walk(const char *base, walk_fn_t walk, void *user, const bool recur) { | ||||
| #ifdef DOCKERBUILD | ||||
|     return false; | ||||
|  |  | |||
|  | @ -108,5 +108,6 @@ bool fs_sys_dir_exists(const char *name); | |||
| bool fs_sys_dir_is_empty(const char *name); | ||||
| bool fs_sys_mkdir(const char *name); // creates with 0777 by default
 | ||||
| bool fs_sys_rmdir(const char *name); // removes an empty directory
 | ||||
| uint64_t fs_sys_get_modified_time(const char *path); | ||||
| 
 | ||||
| #endif // _SM64_FS_H_
 | ||||
|  |  | |||
|  | @ -443,7 +443,7 @@ void smlua_live_reload_update(lua_State* L) { | |||
|             struct ModFile* file = &mod->files[j]; | ||||
| 
 | ||||
|             // check modified time
 | ||||
|             u64 timestamp = mod_get_file_mtime_seconds(file); | ||||
|             u64 timestamp = fs_sys_get_modified_time(file->cachedPath); | ||||
|             if (timestamp <= file->modifiedTimestamp) { continue; } | ||||
| 
 | ||||
|             // update modified time and reload the module
 | ||||
|  |  | |||
|  | @ -17,31 +17,6 @@ | |||
| #include <sys/stat.h> | ||||
| #endif | ||||
| 
 | ||||
| u64 mod_get_file_mtime_seconds(struct ModFile* file) { | ||||
| #ifdef _WIN32 | ||||
|     WIN32_FILE_ATTRIBUTE_DATA fad; | ||||
|     if (!GetFileAttributesExA(file->cachedPath, GetFileExInfoStandard, &fad)) { | ||||
|         // error; you could also GetLastError() here
 | ||||
|         return 0; | ||||
|     } | ||||
|     // FILETIME is 100-ns intervals since 1601-01-01 UTC
 | ||||
|     ULARGE_INTEGER ull; | ||||
|     ull.LowPart  = fad.ftLastWriteTime.dwLowDateTime; | ||||
|     ull.HighPart = fad.ftLastWriteTime.dwHighDateTime; | ||||
| 
 | ||||
|     const u64 EPOCH_DIFF = 116444736000000000ULL; // 100-ns from 1601 to 1970
 | ||||
|     u64 time100ns = ull.QuadPart; | ||||
|     return (time100ns - EPOCH_DIFF) / 10000000ULL;    // to seconds
 | ||||
| #else | ||||
|     struct stat st; | ||||
|     if (stat(file->cachedPath, &st) != 0) { | ||||
|         // error; errno is set
 | ||||
|         return 0; | ||||
|     } | ||||
|     return (u64)st.st_mtime; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| size_t mod_get_lua_size(struct Mod* mod) { | ||||
|     if (!mod) { return 0; } | ||||
|     size_t size = 0; | ||||
|  | @ -178,7 +153,7 @@ void mod_activate(struct Mod* mod) { | |||
|     // activate dynos models
 | ||||
|     for (int i = 0; i < mod->fileCount; i++) { | ||||
|         struct ModFile* file = &mod->files[i]; | ||||
|         file->modifiedTimestamp = mod_get_file_mtime_seconds(file); | ||||
|         file->modifiedTimestamp = fs_sys_get_modified_time(file->cachedPath); | ||||
|         mod_cache_add(mod, file, false); | ||||
| 
 | ||||
|         // forcefully update md5 hash
 | ||||
|  |  | |||
|  | @ -46,7 +46,6 @@ struct Mod { | |||
|     u8 customBehaviorIndex; | ||||
| }; | ||||
| 
 | ||||
| u64 mod_get_file_mtime_seconds(struct ModFile* file); | ||||
| size_t mod_get_lua_size(struct Mod* mod); | ||||
| void mod_activate(struct Mod* mod); | ||||
| void mod_clear(struct Mod* mod); | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 djoslin0
						djoslin0