static void GM_CDECL gmFileInfoOpGetDot(gmThread * a_thread, gmVariable * a_operands) { gmUserObject * user = (gmUserObject *) GM_OBJECT(a_operands->m_value.m_ref); if(user && user->m_user) { gmFileInfoUser * fileInfo = (gmFileInfoUser *) user->m_user; gmStringObject * member = (gmStringObject *) GM_OBJECT(a_operands[1].m_value.m_ref); GM_ASSERT(sizeof(fileInfo->m_creationTime) <= sizeof(gmint)); // NOTE: Not valid or tested for 64bit target if(strcmp(member->GetString(), "creationTime") == 0) a_operands->SetInt((gmint) fileInfo->m_creationTime); else if(strcmp(member->GetString(), "accessedTime") == 0) a_operands->SetInt((gmint) fileInfo->m_accessedTime); else if(strcmp(member->GetString(), "modifiedTime") == 0) a_operands->SetInt((gmint) fileInfo->m_modifiedTime); else if(strcmp(member->GetString(), "size") == 0) a_operands->SetInt((gmint) fileInfo->m_size); else { a_operands->Nullify(); return; } return; } a_operands->Nullify(); }
static int GM_CDECL gmfStringSpanExcluding(gmThread * a_thread) { GM_CHECK_NUM_PARAMS(1); if(a_thread->ParamType(0) == GM_STRING) { const gmVariable * var = a_thread->GetThis(); GM_ASSERT(var->m_type == GM_STRING); gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref); const char * thisStr = (const char *) *strObj; const char * otherStr = a_thread->ParamString(0); int offset = strcspn(thisStr, otherStr); char * buffer = (char *) alloca(offset + 1); memcpy(buffer, thisStr, offset); buffer[offset] = 0; a_thread->PushNewString(buffer, offset); return GM_OK; } return GM_EXCEPTION; }
// string string.SetAt(int a_index, int a_char); // Returns string with modified character at offset, or original string if index out of range. static int GM_CDECL gmStringSetAt(gmThread * a_thread) { GM_CHECK_NUM_PARAMS(1); GM_CHECK_INT_PARAM(index, 0); GM_CHECK_INT_PARAM(newChar, 1); const gmVariable * var = a_thread->GetThis(); GM_ASSERT(var->m_type == GM_STRING); gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref); const char * str = (const char *) *strObj; int strLength = strObj->GetLength(); if(index < 0 || index >= strLength) { a_thread->PushString(strObj); //Return original string if index out of range return GM_OK; } char * buffer = (char *) alloca(strLength + 1); memcpy(buffer, str, strLength + 1); //Copy old string buffer[index] = (char)newChar; //Set character in string a_thread->PushNewString(buffer, strLength); return GM_OK; }
static int GM_CDECL gmStringTrimRight(gmThread * a_thread) { GM_STRING_PARAM(trim, 0, GM_WHITE_SPACE); const gmVariable * var = a_thread->GetThis(); GM_ASSERT(var->m_type == GM_STRING); gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref); const char * str = (const char *) *strObj; int strLength = strObj->GetLength(); if(strLength > 0) { char * buffer = (char *) alloca(strLength + 1); memcpy(buffer, str, strLength + 1); //Copy old string // Find beginning of trailing matches by starting at end char *lpsz = buffer + strLength; while (--lpsz >= buffer && strchr(trim, *lpsz) != NULL) {} ++lpsz; *lpsz = '\0'; a_thread->PushNewString(buffer); } else { a_thread->PushString(strObj); } return GM_OK; }
static int GM_CDECL gmStringGetExtension(gmThread * a_thread) { GM_INT_PARAM(keepDot, 0, 0); const gmVariable * var = a_thread->GetThis(); GM_ASSERT(var->m_type == GM_STRING); gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref); const char * str = (const char *) *strObj; int strLength = strObj->GetLength(); const char *lpsz = str + strLength; while (--lpsz >= str && *lpsz != '.') {} if(*lpsz == '.') { if(!keepDot) { ++lpsz; } a_thread->PushNewString(lpsz); } else { a_thread->PushNewString(""); } return GM_OK; }
int GM_CDECL gmfToInt(gmThread * a_thread) { const gmVariable * var = a_thread->GetThis(); if(GM_INT == var->m_type) { a_thread->PushInt(var->m_value.m_int); } else if (GM_FLOAT == var->m_type) { a_thread->PushInt( (int)var->m_value.m_float); } else if (GM_STRING == var->m_type) { gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref); const char * cstr = * strObj; a_thread->PushInt( atoi(cstr) ); } else { //a_thread->PushInt( 0 ); return GM_EXCEPTION; } return GM_OK; }
int GM_CDECL gmfToString(gmThread * a_thread) { const gmVariable * var = a_thread->GetThis(); if(GM_INT == var->m_type) { char numberAsStringBuffer[64]; sprintf(numberAsStringBuffer, "%d", var->m_value.m_int); // this won't be > 64 chars a_thread->PushNewString(numberAsStringBuffer); } else if (GM_FLOAT == var->m_type) { char numberAsStringBuffer[64]; sprintf(numberAsStringBuffer, "%f", var->m_value.m_float); // this won't be > 64 chars a_thread->PushNewString(numberAsStringBuffer); } else if (GM_STRING == var->m_type) { a_thread->PushString( (gmStringObject *) GM_OBJECT(var->m_value.m_ref) ); } else { return GM_EXCEPTION; } return GM_OK; }
static int GM_CDECL gmStringGetPath(gmThread * a_thread) { GM_INT_PARAM(keepSlash, 0, 0); const gmVariable * var = a_thread->GetThis(); GM_ASSERT(var->m_type == GM_STRING); gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref); const char * str = (const char *) *strObj; int strLength = strObj->GetLength(); char * buffer = (char *) alloca(strLength + 1); memcpy(buffer, str, strLength + 1); //Copy old string char *lpsz = buffer + strLength; while (--lpsz >= buffer && *lpsz != '\\' && *lpsz != '/') {} if(*lpsz == '\\' || *lpsz == '/') { if(keepSlash) lpsz[1] = 0; else lpsz[0] = 0; a_thread->PushNewString(buffer); } else { a_thread->PushNewString(""); } return GM_OK; }
// int string.ReverseFind(char/string a_charOrStringToFind); // Find the last instance of a specific character in a string. // Returns character offset or -1 if not found. static int GM_CDECL gmStringReverseFind(gmThread * a_thread) { GM_CHECK_NUM_PARAMS(1); const char* retCharPtr = NULL; const gmVariable * var = a_thread->GetThis(); GM_ASSERT(var->m_type == GM_STRING); gmStringObject * thisStrObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref); const char* thisStr = (const char *) *thisStrObj; if(a_thread->ParamType(0) == GM_INT) { const char otherChar = (char)a_thread->ParamInt(0); //Find character retCharPtr = strrchr(thisStr, otherChar); } else if(a_thread->ParamType(0) == GM_STRING) { gmStringObject * otherStrObj = a_thread->ParamStringObject(0); const char* otherStr = a_thread->ParamString(0); //Find string const char* lastFoundPtr = NULL; const char* newTestPtr = NULL; const char* curTestPtr = thisStr; const char* endThisStr = thisStr + thisStrObj->GetLength(); int searchStrLength = otherStrObj->GetLength(); //Search through string for last occurence //Not very efficient, but very rarely used. for(;;) { newTestPtr = strstr(curTestPtr, otherStr); if(!newTestPtr) { break; } lastFoundPtr = newTestPtr; curTestPtr = newTestPtr + searchStrLength; if(curTestPtr > endThisStr) { break; } }; retCharPtr = lastFoundPtr; } else { return GM_EXCEPTION; } // return -1 for not found, distance from beginning otherwise int retOffset = (retCharPtr == NULL) ? -1 : (int)(retCharPtr - thisStr); a_thread->PushInt(retOffset); return GM_OK; }
static int GM_CDECL gmfStringIsEmpty(gmThread * a_thread) { const gmVariable * var = a_thread->GetThis(); gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref); a_thread->PushInt( (strObj->GetLength() == 0) ); return GM_OK; }
static void GM_CDECL gmFileFindOpGetDot(gmThread * a_thread, gmVariable * a_operands) { gmUserObject * user = (gmUserObject *) GM_OBJECT(a_operands->m_value.m_ref); if(user && user->m_user) { gmFileFindUser * fileFind = (gmFileFindUser *) user->m_user; gmStringObject * member = (gmStringObject *) GM_OBJECT(a_operands[1].m_value.m_ref); if(strcmp(member->GetString(), "filename") == 0) { a_operands->SetString(a_thread->GetMachine()->AllocStringObject(fileFind->m_findData.cFileName)); return; } else if(strcmp(member->GetString(), "size") == 0) { a_operands->SetInt(fileFind->m_findData.nFileSizeLow); return; } } a_operands->Nullify(); }
static void GM_CDECL gmFileOpGetDot(gmThread * a_thread, gmVariable * a_operands) { gmUserObject * user = (gmUserObject *) GM_OBJECT(a_operands->m_value.m_ref); if(user && user->m_user) { gmStringObject * member = (gmStringObject *) GM_OBJECT(a_operands[1].m_value.m_ref); if(strcmp(member->GetString(), "SEEK_CUR") == 0) a_operands->SetInt(SEEK_CUR); else if(strcmp(member->GetString(), "SEEK_END") == 0) a_operands->SetInt(SEEK_END); else if(strcmp(member->GetString(), "SEEK_SET") == 0) a_operands->SetInt(SEEK_SET); else { a_operands->Nullify(); return; } return; } a_operands->Nullify(); }
static int GM_CDECL gmStringGetFilename(gmThread * a_thread) { const gmVariable * var = a_thread->GetThis(); GM_ASSERT(var->m_type == GM_STRING); gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref); const char * str = (const char *) *strObj; int strLength = strObj->GetLength(); const char *lpsz = str + strLength; while (--lpsz >= str && *lpsz != '\\' && *lpsz != '/') {} ++lpsz; a_thread->PushNewString(lpsz); return GM_OK; }
static int GM_CDECL gmStringTrimLeft(gmThread * a_thread) { GM_STRING_PARAM(trim, 0, GM_WHITE_SPACE); const gmVariable * var = a_thread->GetThis(); GM_ASSERT(var->m_type == GM_STRING); gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref); const char * str = (const char *) *strObj; if(strlen(str) > 0) { while(*str && strchr(trim, *str)) ++str; a_thread->PushNewString(str); } else a_thread->PushString(strObj); return GM_OK; }
static int GM_CDECL gmfStringMid(gmThread * a_thread) { GM_CHECK_NUM_PARAMS(2); int first = 0, count = 0; if(!gmGetFloatOrIntParamAsInt(a_thread, 0, first)) {return GM_EXCEPTION;} if(!gmGetFloatOrIntParamAsInt(a_thread, 1, count)) {return GM_EXCEPTION;} const gmVariable * var = a_thread->GetThis(); GM_ASSERT(var->m_type == GM_STRING); gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref); const char * str = (const char *) *strObj; int length = strObj->GetLength(); //Check bounds if (first < 0) { first = 0; } if (count < 0) { count = 0; } if (first + count > length) { count = length - first; } if (first > length) { count = 0; } char * buffer = (char *) alloca(count + 1); memcpy(buffer, str + first, count); buffer[count] = 0; a_thread->PushNewString(buffer); return GM_OK; }
// string.RemoveInvalidChars(a_replaceChar, a_invalidSet) // eg. "File Name#1.tga".RemoveInvalidChars("_","# ") returns "File_Name_1.tga" // Note: Parameters are optional. static int GM_CDECL gmfStringReplaceCharsInSet(gmThread * a_thread) { GM_INT_PARAM(repCharInt, 0, '_'); GM_STRING_PARAM(invalidCharSet, 1, " \\/:-+"); char repChar = (char)repCharInt; //Convert full int to char const gmVariable * varA = a_thread->GetThis(); GM_ASSERT(varA->m_type == GM_STRING); gmStringObject * strObjA = (gmStringObject *) GM_OBJECT(varA->m_value.m_ref); const char* cStrA = strObjA->GetString(); int lenA = strObjA->GetLength(); //Alloc buffer on stack is fine, path strings cannot be long char * buffer = (char *) alloca(lenA + 1); memcpy(buffer, cStrA, lenA + 1); int validPos; //Check that replacement char is NOT in invalid set, otherwise endless loop... if(strchr(invalidCharSet, repChar)) { return GM_EXCEPTION; } for(;;) { validPos = strcspn(buffer, invalidCharSet); if(validPos != lenA) { buffer[validPos] = repChar; } else { break; } } a_thread->PushNewString(buffer, lenA); return GM_OK; }
static int GM_CDECL gmfStringUpper(gmThread * a_thread) { const gmVariable * var = a_thread->GetThis(); GM_ASSERT(var->m_type == GM_STRING); gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref); const char * str = (const char *) *strObj; int length = strObj->GetLength(); char * buffer = (char *) alloca(length + 1); memcpy(buffer, str, length + 1); strupr(buffer); a_thread->PushNewString(buffer, length); return GM_OK; }
static int GM_CDECL gmfStringCompareNoCase(gmThread * a_thread) { GM_CHECK_NUM_PARAMS(1); if(a_thread->ParamType(0) == GM_STRING) { const gmVariable * var = a_thread->GetThis(); GM_ASSERT(var->m_type == GM_STRING); gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref); const char* thisStr = (const char *) *strObj; const char* otherStr = a_thread->ParamString(0); a_thread->PushInt(_gmstricmp(thisStr, otherStr)); return GM_OK; } return GM_EXCEPTION; }
// int string.GetAt(int a_index); // Returns character at offset or null if index out of range. static int GM_CDECL gmStringGetAt(gmThread * a_thread) { GM_CHECK_NUM_PARAMS(1); GM_CHECK_INT_PARAM(index, 0); const gmVariable * var = a_thread->GetThis(); GM_ASSERT(var->m_type == GM_STRING); gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref); const char * str = (const char *) *strObj; if(index < 0 || index >= strObj->GetLength()) { a_thread->PushNull(); //Return null if index out of range return GM_OK; } a_thread->PushInt(str[index]); return GM_OK; }
static int GM_CDECL gmStringSetExtension(gmThread * a_thread) { GM_STRING_PARAM(newExt, 0, ""); const gmVariable * var = a_thread->GetThis(); GM_ASSERT(var->m_type == GM_STRING); gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref); const char * str = (const char *) *strObj; int strLength = strObj->GetLength(); int extLength = strlen(newExt); if (extLength && newExt[0] == '.') { ++newExt; extLength = strlen(newExt); } char *buffer = (char *) alloca(strLength + 1 + extLength); memcpy(buffer, str, strLength + 1); char *lpsz = buffer + strLength; while (--lpsz >= buffer && *lpsz != '.') {} if(*lpsz == '.') { *lpsz = '\0'; if (extLength) sprintf(buffer, "%s.%s", buffer, newExt); } else if (extLength) { sprintf(buffer, "%s.%s", buffer, newExt); } a_thread->PushNewString(buffer); return GM_OK; }
// int string[int index] // Note: Because strings are constant and in a table, it is impossible to implement SetInd. static void GM_CDECL gmStringOpGetInd(gmThread * a_thread, gmVariable * a_operands) { if( a_operands[0].m_type != GM_STRING || a_operands[1].m_type != GM_INT ) { a_operands->Nullify(); return; } gmStringObject * strObjA = (gmStringObject *) GM_OBJECT(a_operands[0].m_value.m_ref); const char* cStrA = strObjA->GetString(); int index = a_operands[1].m_value.m_int; if( index < 0 || index > strObjA->GetLength()-1 ) { a_operands->Nullify(); } else { a_operands->SetInt( (int)cStrA[index] ); } }
static int GM_CDECL gmStringReverse(gmThread * a_thread) { const gmVariable * var = a_thread->GetThis(); GM_ASSERT(var->m_type == GM_STRING); gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref); const char * str = (const char *) *strObj; int len = strlen(str); if(len > 0) { char * buffer = (char *) alloca(len + 1); memcpy(buffer, str, len + 1); //Copy old string while(len--) { buffer[len] = *(str++); } a_thread->PushNewString(buffer); } return GM_OK; }
static int GM_CDECL gmStringGetFilenameNoExt(gmThread * a_thread) { const gmVariable * var = a_thread->GetThis(); GM_ASSERT(var->m_type == GM_STRING); gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref); const char * str = (const char *) *strObj; int strLength = strObj->GetLength(); char * buffer = (char *) alloca(strLength + 1); memcpy(buffer, str, strLength + 1); //Copy old string char *lpsz = buffer + strLength; while (--lpsz >= buffer && *lpsz != '\\' && *lpsz != '/') {} buffer = ++lpsz; strLength = strlen(buffer); lpsz = buffer + strLength; while (--lpsz >= buffer && *lpsz != '.') {} if(*lpsz == '.') *lpsz = 0; a_thread->PushNewString(buffer); return GM_OK; }
static int GM_CDECL gmfStringRightAt(gmThread * a_thread) { GM_CHECK_NUM_PARAMS(1); GM_CHECK_INT_PARAM(index, 0); const gmVariable * var = a_thread->GetThis(); GM_ASSERT(var->m_type == GM_STRING); gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref); const char * str = (const char *) *strObj; int length = strObj->GetLength(); index = gmClamp(0, index, length); int count = (length - index); char * buffer = (char *) alloca(count + 1); memcpy(buffer, str + index, count); buffer[count] = 0; a_thread->PushNewString(buffer, count); return GM_OK; }
// int string.Find(char/string a_charOrStringToFind, int a_startOffset == 0); // Find a character or character string in a string. // Returns character offset or -1 if not found. static int GM_CDECL gmStringFind(gmThread * a_thread) { int numParams = GM_THREAD_ARG->GetNumParams(); int startOffset = 0; char* retCharPtr = NULL; const gmVariable * var = a_thread->GetThis(); GM_ASSERT(var->m_type == GM_STRING); gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref); const char* thisStr = (const char *) *strObj; if(numParams == 2) { if(a_thread->ParamType(1) == GM_INT) { startOffset = a_thread->ParamInt(1); //Optional start offset param } else { return GM_EXCEPTION; } } else if(numParams < 1 || numParams > 2) { return GM_EXCEPTION; } //Check if this string is empty, or start offset out of range if( (strObj->GetLength() == 0) || (startOffset > strObj->GetLength()) || (startOffset < 0) ) { a_thread->PushInt( -1 ); //return Not Found return GM_OK; } if(a_thread->ParamType(0) == GM_INT) { const char otherChar = (char)a_thread->ParamInt(0); //Find character retCharPtr = (char*)strchr(thisStr + startOffset, otherChar); } else if(a_thread->ParamType(0) == GM_STRING) { const char* otherStr = a_thread->ParamString(0); //Find string retCharPtr = (char*)strstr(thisStr + startOffset, otherStr); } else { return GM_EXCEPTION; } // return -1 for not found, distance from beginning otherwise int retOffset = (retCharPtr == NULL) ? -1 : (int)(retCharPtr - thisStr); a_thread->PushInt(retOffset); return GM_OK; }
// string.AppendPath(a_appendString, a_endWithSlash); // Append this string with another string, fixing for slashes // a_endWithSlash is optional, default is false, removing trailing slashes. static int GM_CDECL gmfStringAppendPath(gmThread * a_thread) { //Need at least 1 parameter if(a_thread->GetNumParams() < 1) { return GM_EXCEPTION; } //Optional trailing slash flag int PutTrailingSlash = a_thread->ParamInt(1, false); if(a_thread->ParamType(0) == GM_STRING) { const gmVariable * varA = a_thread->GetThis(); GM_ASSERT(varA->m_type == GM_STRING); gmStringObject * strObjA = (gmStringObject *) GM_OBJECT(varA->m_value.m_ref); gmStringObject * strObjB = a_thread->ParamStringObject(0); const char* cStrA = strObjA->GetString(); const char* cStrB = strObjB->GetString(); int lenA = strObjA->GetLength(); int lenB = strObjB->GetLength(); int curLength = lenA; int appLength = lenB; //Alloc buffer on stack is fine, path strings cannot be long char * buffer = (char *) alloca(curLength + appLength + 2); if(lenA > 0) { memcpy(buffer, cStrA, lenA); //Make sure first part has a slash if(buffer[curLength-1] != '\\' && buffer[curLength-1] != '/') { buffer[curLength] = '\\'; curLength += 1; } } if(lenB > 0) { //Remove slash from start of append string const char* startOfAppend = cStrB; if((startOfAppend[0] == '\\') || (startOfAppend[0] == '/')) { startOfAppend += 1; appLength -= 1; } //Append the string memcpy(&buffer[curLength], startOfAppend, appLength); } int newLength = curLength + appLength; if(PutTrailingSlash && (newLength > 0)) //Only add slash if string is not empty { //Make sure path ends with slash if(buffer[newLength-1] != '\\' && buffer[newLength-1] != '/') { buffer[newLength] = '\\'; newLength += 1; } } else { //Make sure path does not end with slash if(buffer[newLength-1] == '\\' || buffer[newLength-1] == '/') { newLength -= 1; } } //Make sure it is terminated buffer[newLength] = 0; a_thread->PushNewString(buffer, newLength); return GM_OK; } return GM_EXCEPTION; }
// string.AppendPath(a_appendString, a_endWithSlash); // Append this string with another string, fixing for slashes // a_endWithSlash is optional, default is false, removing trailing slashes. static void GM_CDECL gmStringOpAppendPath(gmThread * a_thread, gmVariable * a_operands) { // Both types must be strings if(a_operands[0].m_type != GM_STRING || a_operands[1].m_type != GM_STRING) { a_operands[0].m_type = GM_NULL; a_operands[0].m_value.m_ref = 0; return; } gmStringObject * strObjA = (gmStringObject *) GM_OBJECT(a_operands[0].m_value.m_ref); gmStringObject * strObjB = (gmStringObject *) GM_OBJECT(a_operands[1].m_value.m_ref); const char* cStrA = strObjA->GetString(); const char* cStrB = strObjB->GetString(); int lenA = strObjA->GetLength(); int lenB = strObjB->GetLength(); int curLength = lenA; int appLength = lenB; //Alloc buffer on stack is fine, path strings cannot be long char * buffer = (char *) alloca(curLength + appLength + 2); if(lenA <= 0) { a_operands[0] = a_operands[1]; } if(lenB <= 0) { return; } memcpy(buffer, cStrA, lenA); //Make sure first part has a slash if(buffer[curLength-1] != '\\' && buffer[curLength-1] != '/') { buffer[curLength] = '\\'; curLength += 1; } //Remove slash from start of append string const char* startOfAppend = cStrB; if((startOfAppend[0] == '\\') || (startOfAppend[0] == '/')) { startOfAppend += 1; appLength -= 1; } //Append the string memcpy(&buffer[curLength], startOfAppend, appLength); int newLength = curLength + appLength; //Make sure it is terminated buffer[newLength] = 0; a_operands[0].m_type = GM_STRING; a_operands[0].m_value.m_ref = a_thread->GetMachine()->AllocStringObject(buffer, newLength)->GetRef(); }