mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2025-10-30 08:01:01 +00:00
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>
540 lines
15 KiB
C++
540 lines
15 KiB
C++
#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 //
|
|
//////////
|
|
|
|
s64 DynOS_Misc_ParseInteger(const String& _Arg, bool* found) {
|
|
const char* argStr = _Arg.begin();
|
|
if (argStr[0] == '0' && argStr[1] == 'x') {
|
|
// is a hex number
|
|
argStr += 2;
|
|
while(*argStr != '\0') {
|
|
if (*argStr >= '0' && *argStr <= '9') {
|
|
// good
|
|
} else if (*argStr >= 'a' && *argStr <= 'f') {
|
|
// good
|
|
} else if (*argStr >= 'A' && *argStr <= 'F') {
|
|
// good
|
|
} else {
|
|
// bad character
|
|
*found = false;
|
|
return 0;
|
|
}
|
|
argStr++;
|
|
}
|
|
} else {
|
|
// is a decimal number
|
|
if (*argStr == '-' || *argStr == '+') {
|
|
// good
|
|
argStr++;
|
|
}
|
|
while(*argStr != '\0') {
|
|
if (*argStr >= '0' && *argStr <= '9') {
|
|
// good
|
|
} else if (*argStr == '.') {
|
|
// good
|
|
} else {
|
|
// bad character
|
|
*found = false;
|
|
return 0;
|
|
}
|
|
argStr++;
|
|
}
|
|
}
|
|
|
|
*found = true;
|
|
return _Arg.ParseInt();
|
|
}
|
|
|
|
void DynOS_Gfx_Free(GfxData* aGfxData) {
|
|
if (aGfxData) {
|
|
for (auto& _Node : aGfxData->mLights) {
|
|
Delete(_Node->mData);
|
|
Delete(_Node);
|
|
}
|
|
for (auto& _Node : aGfxData->mLight0s) {
|
|
Delete(_Node->mData);
|
|
Delete(_Node);
|
|
}
|
|
for (auto& _Node : aGfxData->mLightTs) {
|
|
Delete(_Node->mData);
|
|
Delete(_Node);
|
|
}
|
|
for (auto& _Node : aGfxData->mAmbientTs) {
|
|
Delete(_Node->mData);
|
|
Delete(_Node);
|
|
}
|
|
for (auto& _Node : aGfxData->mTextures) {
|
|
Delete(_Node->mData);
|
|
Delete(_Node);
|
|
}
|
|
for (auto& _Node : aGfxData->mTextureLists) {
|
|
Delete(_Node->mData);
|
|
Delete(_Node);
|
|
}
|
|
for (auto& _Node : aGfxData->mVertices) {
|
|
Delete(_Node->mData);
|
|
Delete(_Node);
|
|
}
|
|
for (auto& _Node : aGfxData->mDisplayLists) {
|
|
Delete(_Node->mData);
|
|
Delete(_Node);
|
|
}
|
|
for (auto& _Node : aGfxData->mGeoLayouts) {
|
|
Delete(_Node->mData);
|
|
Delete(_Node);
|
|
}
|
|
for (auto& _Node : aGfxData->mAnimations) {
|
|
Delete(_Node->mData);
|
|
Delete(_Node);
|
|
}
|
|
for (auto& _Node : aGfxData->mCollisions) {
|
|
Delete(_Node->mData);
|
|
Delete(_Node);
|
|
}
|
|
for (auto& _Node : aGfxData->mBehaviorScripts) {
|
|
Delete(_Node->mData);
|
|
Delete(_Node);
|
|
}
|
|
for (auto& _Node : aGfxData->mLevelScripts) {
|
|
Delete(_Node->mData);
|
|
Delete(_Node);
|
|
}
|
|
for (auto& _Node : aGfxData->mMacroObjects) {
|
|
Delete(_Node->mData);
|
|
Delete(_Node);
|
|
}
|
|
for (auto& _Node : aGfxData->mTrajectories) {
|
|
Delete(_Node->mData);
|
|
Delete(_Node);
|
|
}
|
|
for (auto& _Node : aGfxData->mMovtexs) {
|
|
Delete(_Node->mData);
|
|
Delete(_Node);
|
|
}
|
|
for (auto& _Node : aGfxData->mMovtexQCs) {
|
|
Delete(_Node->mData);
|
|
Delete(_Node);
|
|
}
|
|
for (auto& _Node : aGfxData->mRooms) {
|
|
Delete(_Node->mData);
|
|
Delete(_Node);
|
|
}
|
|
Delete(aGfxData);
|
|
}
|
|
}
|
|
|
|
u32 DynOS_Lua_RememberVariable(GfxData* aGfxData, void* aPtr, const String& token) {
|
|
// remember as lua pointer
|
|
aGfxData->mLuaPointerList.Add(aPtr);
|
|
|
|
// find existing token
|
|
for (u32 i = 0; i < aGfxData->mLuaTokenList.Count(); i++) {
|
|
if (aGfxData->mLuaTokenList[i] == token) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
// add token
|
|
aGfxData->mLuaTokenList.Add(token);
|
|
return aGfxData->mLuaTokenList.Count() - 1;
|
|
}
|
|
|
|
///////////////////////
|
|
// Recursive Descent //
|
|
///////////////////////
|
|
|
|
static char* sRdString = NULL;
|
|
static bool sRdError = false;
|
|
static RDConstantFunc sRdConstantFunc = NULL;
|
|
|
|
static s64 ParseExpression();
|
|
|
|
static void ParseWhitespace() {
|
|
while (*sRdString == ' ' || *sRdString == '\t' || *sRdString == '\r' || *sRdString == '\n') {
|
|
sRdString++;
|
|
}
|
|
}
|
|
|
|
static bool IsAlphabetical(char c) {
|
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
|
}
|
|
|
|
static bool IsAlphaNumeric(char c) {
|
|
return IsAlphabetical(c) || (c >= '0' && c <= '9');
|
|
}
|
|
|
|
static bool IsIdentifierBeginning(char c) {
|
|
return IsAlphabetical(c) || (c == '_');
|
|
}
|
|
|
|
static bool IsIdentifierCharacter(char c) {
|
|
return IsAlphaNumeric(c) || (c == '_');
|
|
}
|
|
|
|
static bool ParseOperator(const char* op) {
|
|
size_t opLen = strlen(op);
|
|
if (!strncmp(sRdString, op, opLen)) {
|
|
sRdString += opLen;
|
|
ParseWhitespace();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool PeekOperator(const char* op) {
|
|
size_t opLen = strlen(op);
|
|
return (!strncmp(sRdString, op, opLen));
|
|
}
|
|
|
|
static s64 ParseNumeric() {
|
|
String numeric = "";
|
|
char* c = sRdString;
|
|
|
|
if (*c == '0' && *(c+1) == 'x') {
|
|
// is hex
|
|
numeric.Add(*c);
|
|
c++;
|
|
numeric.Add(*c);
|
|
c++;
|
|
while (true) {
|
|
if (*c >= '0' && *c <= '9') {
|
|
// good
|
|
} else if (*c >= 'a' && *c <= 'f') {
|
|
// good
|
|
} else if (*c >= 'A' && *c <= 'F') {
|
|
// good
|
|
} else {
|
|
// bad
|
|
break;
|
|
}
|
|
numeric.Add(*c);
|
|
c++;
|
|
}
|
|
} else {
|
|
// is decimal
|
|
while (*c >= '0' && *c <= '9') {
|
|
numeric.Add(*c);
|
|
c++;
|
|
}
|
|
}
|
|
|
|
// advance parsing
|
|
sRdString = c;
|
|
ParseWhitespace();
|
|
|
|
// parse
|
|
return numeric.ParseInt();
|
|
}
|
|
|
|
static s64 ParseFactor() {
|
|
char* c = sRdString;
|
|
|
|
// check for unary op
|
|
if (ParseOperator("-")) {
|
|
return -ParseFactor();
|
|
} else if (ParseOperator("+")) {
|
|
return +ParseFactor();
|
|
} else if (ParseOperator("!")) {
|
|
return !ParseFactor();
|
|
} else if (ParseOperator("~")) {
|
|
return ~ParseFactor();
|
|
}
|
|
|
|
// check for numeric
|
|
if (*c >= '0' && *c <= '9') {
|
|
return ParseNumeric();
|
|
}
|
|
|
|
// check for sub expression
|
|
if (ParseOperator("(")) {
|
|
s64 e1 = ParseExpression();
|
|
if (ParseOperator(")")) {
|
|
return e1;
|
|
} else {
|
|
sRdError = true;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// check for known identifier
|
|
if (IsIdentifierBeginning(*c)) {
|
|
String identifier = "";
|
|
char* cTmp = c;
|
|
while (IsIdentifierCharacter(*cTmp)) {
|
|
identifier.Add(*cTmp);
|
|
cTmp++;
|
|
}
|
|
|
|
if (sRdConstantFunc != NULL) {
|
|
bool constantFound = false;
|
|
s64 constantValue = sRdConstantFunc(identifier, &constantFound);
|
|
if (constantFound) {
|
|
sRdString = cTmp;
|
|
return constantValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
sRdError = true;
|
|
return 0;
|
|
}
|
|
|
|
static s64 ParseTerm() {
|
|
s64 f1 = ParseFactor();
|
|
|
|
while (PeekOperator("*") || PeekOperator("/") || PeekOperator("%")) {
|
|
if (ParseOperator("*")) {
|
|
f1 *= ParseFactor();
|
|
} else if (ParseOperator("/")) {
|
|
f1 /= ParseFactor();
|
|
} else if (ParseOperator("%")) {
|
|
f1 %= ParseFactor();
|
|
}
|
|
}
|
|
|
|
return f1;
|
|
}
|
|
|
|
static s64 ParseAddSubExpression() {
|
|
s64 t1 = ParseTerm();
|
|
|
|
while (PeekOperator("+") || PeekOperator("-")) {
|
|
if (ParseOperator("+")) {
|
|
t1 += ParseTerm();
|
|
} else if (ParseOperator("-")) {
|
|
t1 -= ParseTerm();
|
|
}
|
|
}
|
|
|
|
return t1;
|
|
}
|
|
|
|
static s64 ParseShiftExpression() {
|
|
s64 e1 = ParseAddSubExpression();
|
|
|
|
while (PeekOperator("<<") || PeekOperator(">>")) {
|
|
if (ParseOperator("<<")) {
|
|
e1 = e1 << ParseAddSubExpression();
|
|
} else if (ParseOperator(">>")) {
|
|
e1 = e1 >> ParseAddSubExpression();
|
|
}
|
|
}
|
|
|
|
return e1;
|
|
}
|
|
|
|
static s64 ParseBitAndExpression() {
|
|
s64 e1 = ParseShiftExpression();
|
|
|
|
while (PeekOperator("&")) {
|
|
if (ParseOperator("&")) {
|
|
e1 &= ParseShiftExpression();
|
|
}
|
|
}
|
|
|
|
return e1;
|
|
}
|
|
|
|
static s64 ParseBitXorExpression() {
|
|
s64 e1 = ParseBitAndExpression();
|
|
|
|
while (PeekOperator("^")) {
|
|
if (ParseOperator("^")) {
|
|
e1 ^= ParseBitAndExpression();
|
|
}
|
|
}
|
|
|
|
return e1;
|
|
}
|
|
|
|
static s64 ParseBitOrExpression() {
|
|
s64 e1 = ParseBitXorExpression();
|
|
|
|
while (PeekOperator("|")) {
|
|
if (ParseOperator("|")) {
|
|
e1 |= ParseBitXorExpression();
|
|
}
|
|
}
|
|
|
|
return e1;
|
|
}
|
|
|
|
static s64 ParseExpression() {
|
|
return ParseBitOrExpression();
|
|
}
|
|
|
|
s64 DynOS_RecursiveDescent_Parse(const char* expr, bool* success, RDConstantFunc func) {
|
|
sRdString = (char*)expr;
|
|
sRdError = false;
|
|
sRdConstantFunc = func;
|
|
s64 value = ParseExpression();
|
|
if (strlen(sRdString) > 0) {
|
|
sRdError = true;
|
|
}
|
|
sRdString = NULL;
|
|
*success = !sRdError;
|
|
return value;
|
|
}
|