Browse Source

Further improvement of case-insensitive file opening

Lou Yihua 6 years ago
parent
commit
cd314c930d
10 changed files with 177 additions and 62 deletions
  1. 1 4
      audio.c
  2. 19 19
      global.c
  3. 1 1
      global.h
  4. 1 4
      mp3play.c
  5. 2 2
      palcfg.c
  6. 2 0
      rixplay.cpp
  7. 1 1
      ui.c
  8. 2 2
      uigame.c
  9. 108 28
      util.c
  10. 40 1
      util.h

+ 1 - 4
audio.c

@@ -259,10 +259,7 @@ AUDIO_OpenDevice(
    switch (gConfig.eMusicType)
    switch (gConfig.eMusicType)
    {
    {
    case MUSIC_RIX:
    case MUSIC_RIX:
-       if (!(gAudioDevice.pMusPlayer = RIX_Init(PAL_CombinePath(0, gConfig.pszGamePath, "mus.mkf"))))
-       {
-           gAudioDevice.pMusPlayer = RIX_Init(PAL_CombinePath(0, gConfig.pszGamePath, "MUS.MKF"));
-       }
+       gAudioDevice.pMusPlayer = RIX_Init(UTIL_GetFullPathName(PAL_BUFFER_SIZE_ARGS(0), gConfig.pszGamePath, "mus.mkf"));
 	   break;
 	   break;
    case MUSIC_MP3:
    case MUSIC_MP3:
 	   gAudioDevice.pMusPlayer = MP3_Init();
 	   gAudioDevice.pMusPlayer = MP3_Init();

+ 19 - 19
global.c

@@ -202,7 +202,7 @@ PAL_InitGlobals(
    //
    //
    Decompress = gConfig.fIsWIN95 ? YJ2_Decompress : YJ1_Decompress;
    Decompress = gConfig.fIsWIN95 ? YJ2_Decompress : YJ1_Decompress;
 
 
-   gpGlobals->lpObjectDesc = gConfig.fIsWIN95 ? NULL : PAL_LoadObjectDesc(PAL_CombinePath(0, gConfig.pszGamePath, "desc.dat"));
+   gpGlobals->lpObjectDesc = gConfig.fIsWIN95 ? NULL : PAL_LoadObjectDesc("desc.dat");
    gpGlobals->bCurrentSaveSlot = 1;
    gpGlobals->bCurrentSaveSlot = 1;
 
 
    return 0;
    return 0;
@@ -567,7 +567,7 @@ typedef struct tagSAVEDGAME_WIN
 
 
 static BOOL
 static BOOL
 PAL_LoadGame_Common(
 PAL_LoadGame_Common(
-	const char         *szFileName,
+	int                 iSaveSlot,
 	LPSAVEDGAME_COMMON  s,
 	LPSAVEDGAME_COMMON  s,
 	size_t              size
 	size_t              size
 )
 )
@@ -575,7 +575,7 @@ PAL_LoadGame_Common(
 	//
 	//
 	// Try to open the specified file
 	// Try to open the specified file
 	//
 	//
-	FILE *fp = fopen(szFileName, "rb");
+	FILE *fp = UTIL_OpenFileAtPath(gConfig.pszSavePath, PAL_va(1, "%d.rpg", iSaveSlot));
 	//
 	//
 	// Read all data from the file and close.
 	// Read all data from the file and close.
 	//
 	//
@@ -647,7 +647,7 @@ PAL_LoadGame_Common(
 
 
 static INT
 static INT
 PAL_LoadGame_DOS(
 PAL_LoadGame_DOS(
-   LPCSTR         szFileName
+   int            iSaveSlot
 )
 )
 /*++
 /*++
   Purpose:
   Purpose:
@@ -670,7 +670,7 @@ PAL_LoadGame_DOS(
    //
    //
    // Get all the data from the saved game struct.
    // Get all the data from the saved game struct.
    //
    //
-   if (!PAL_LoadGame_Common(szFileName, (LPSAVEDGAME_COMMON)&s, sizeof(SAVEDGAME_DOS)))
+   if (!PAL_LoadGame_Common(iSaveSlot, (LPSAVEDGAME_COMMON)&s, sizeof(SAVEDGAME_DOS)))
 	   return -1;
 	   return -1;
 
 
    //
    //
@@ -699,7 +699,7 @@ PAL_LoadGame_DOS(
 
 
 static INT
 static INT
 PAL_LoadGame_WIN(
 PAL_LoadGame_WIN(
-   LPCSTR         szFileName
+   int            iSaveSlot
 )
 )
 /*++
 /*++
   Purpose:
   Purpose:
@@ -721,7 +721,7 @@ PAL_LoadGame_WIN(
    //
    //
    // Get all the data from the saved game struct.
    // Get all the data from the saved game struct.
    //
    //
-   if (!PAL_LoadGame_Common(szFileName, (LPSAVEDGAME_COMMON)&s, sizeof(SAVEDGAME_WIN)))
+   if (!PAL_LoadGame_Common(iSaveSlot, (LPSAVEDGAME_COMMON)&s, sizeof(SAVEDGAME_WIN)))
 	   return -1;
 	   return -1;
 
 
    memcpy(gpGlobals->g.rgObject, s.rgObject, sizeof(gpGlobals->g.rgObject));
    memcpy(gpGlobals->g.rgObject, s.rgObject, sizeof(gpGlobals->g.rgObject));
@@ -735,15 +735,15 @@ PAL_LoadGame_WIN(
 
 
 static INT
 static INT
 PAL_LoadGame(
 PAL_LoadGame(
-   LPCSTR         szFileName
+   int            iSaveSlot
 )
 )
 {
 {
-	return gConfig.fIsWIN95 ? PAL_LoadGame_WIN(szFileName) : PAL_LoadGame_DOS(szFileName);
+	return gConfig.fIsWIN95 ? PAL_LoadGame_WIN(iSaveSlot) : PAL_LoadGame_DOS(iSaveSlot);
 }
 }
 
 
 static VOID
 static VOID
 PAL_SaveGame_Common(
 PAL_SaveGame_Common(
-	LPCSTR             szFileName,
+	int                iSaveSlot,
 	WORD               wSavedTimes,
 	WORD               wSavedTimes,
 	LPSAVEDGAME_COMMON s,
 	LPSAVEDGAME_COMMON s,
 	size_t             size
 	size_t             size
@@ -798,7 +798,7 @@ PAL_SaveGame_Common(
 	//
 	//
 	// Try writing to file
 	// Try writing to file
 	//
 	//
-	if ((fp = fopen(szFileName, "wb")) == NULL)
+	if ((fp = UTIL_OpenFileAtPathForMode(gConfig.pszSavePath, PAL_va(1, "%d.rpg", iSaveSlot), "wb")) == NULL)
 	{
 	{
 		return;
 		return;
 	}
 	}
@@ -812,7 +812,7 @@ PAL_SaveGame_Common(
 
 
 static VOID
 static VOID
 PAL_SaveGame_DOS(
 PAL_SaveGame_DOS(
-   LPCSTR         szFileName,
+   int            iSaveSlot,
    WORD           wSavedTimes
    WORD           wSavedTimes
 )
 )
 /*++
 /*++
@@ -849,12 +849,12 @@ PAL_SaveGame_DOS(
    //
    //
    // Put all the data to the saved game struct.
    // Put all the data to the saved game struct.
    //
    //
-   PAL_SaveGame_Common(szFileName, wSavedTimes, (LPSAVEDGAME_COMMON)&s, sizeof(SAVEDGAME_DOS));
+   PAL_SaveGame_Common(iSaveSlot, wSavedTimes, (LPSAVEDGAME_COMMON)&s, sizeof(SAVEDGAME_DOS));
 }
 }
 
 
 static VOID
 static VOID
 PAL_SaveGame_WIN(
 PAL_SaveGame_WIN(
-   LPCSTR         szFileName,
+   int            iSaveSlot,
    WORD           wSavedTimes
    WORD           wSavedTimes
 )
 )
 /*++
 /*++
@@ -880,19 +880,19 @@ PAL_SaveGame_WIN(
    memcpy(s.rgObject, gpGlobals->g.rgObject, sizeof(gpGlobals->g.rgObject));
    memcpy(s.rgObject, gpGlobals->g.rgObject, sizeof(gpGlobals->g.rgObject));
    memcpy(s.rgEventObject, gpGlobals->g.lprgEventObject, sizeof(EVENTOBJECT) * gpGlobals->g.nEventObject);
    memcpy(s.rgEventObject, gpGlobals->g.lprgEventObject, sizeof(EVENTOBJECT) * gpGlobals->g.nEventObject);
 
 
-   PAL_SaveGame_Common(szFileName, wSavedTimes, (LPSAVEDGAME_COMMON)&s, sizeof(SAVEDGAME_WIN));
+   PAL_SaveGame_Common(iSaveSlot, wSavedTimes, (LPSAVEDGAME_COMMON)&s, sizeof(SAVEDGAME_WIN));
 }
 }
 
 
 VOID
 VOID
 PAL_SaveGame(
 PAL_SaveGame(
-   LPCSTR         szFileName,
+   int            iSaveSlot,
    WORD           wSavedTimes
    WORD           wSavedTimes
 )
 )
 {
 {
 	if (gConfig.fIsWIN95)
 	if (gConfig.fIsWIN95)
-		PAL_SaveGame_WIN(szFileName, wSavedTimes);
+		PAL_SaveGame_WIN(iSaveSlot, wSavedTimes);
 	else
 	else
-		PAL_SaveGame_DOS(szFileName, wSavedTimes);
+		PAL_SaveGame_DOS(iSaveSlot, wSavedTimes);
 }
 }
 
 
 VOID
 VOID
@@ -921,7 +921,7 @@ PAL_InitGameData(
    //
    //
    // try loading from the saved game file.
    // try loading from the saved game file.
    //
    //
-   if (iSaveSlot == 0 || PAL_LoadGame(PAL_CombinePath(0, gConfig.pszSavePath, PAL_va(1, "%d.rpg", iSaveSlot))) != 0)
+   if (iSaveSlot == 0 || PAL_LoadGame(iSaveSlot) != 0)
    {
    {
       //
       //
       // Cannot load the saved game file. Load the defaults.
       // Cannot load the saved game file. Load the defaults.

+ 1 - 1
global.h

@@ -571,7 +571,7 @@ PAL_FreeGlobals(
 
 
 VOID
 VOID
 PAL_SaveGame(
 PAL_SaveGame(
-   LPCSTR        szFileName,
+   int           iSaveSlot,
    WORD          wSavedTimes
    WORD          wSavedTimes
 );
 );
 
 

+ 1 - 4
mp3play.c

@@ -113,10 +113,7 @@ MP3_Play(
 
 
 	if (iNum > 0)
 	if (iNum > 0)
 	{
 	{
-		if ((player->pMP3 = mad_openFile(PAL_CombinePath(0, gConfig.pszGamePath, PAL_va(1, "mp3/%.2d.mp3", iNum)), AUDIO_GetDeviceSpec(), gConfig.iResampleQuality)) == NULL)
-		{
-			player->pMP3 = mad_openFile(PAL_CombinePath(0, gConfig.pszGamePath, PAL_va(1, "MP3/%.2d.MP3", iNum)), AUDIO_GetDeviceSpec(), gConfig.iResampleQuality);
-		}
+		player->pMP3 = mad_openFile(UTIL_GetFullPathName(PAL_BUFFER_SIZE_ARGS(0), gConfig.pszGamePath, PAL_va(1, "mp3/%.2d.mp3", iNum)), AUDIO_GetDeviceSpec(), gConfig.iResampleQuality);
 
 
 		if (player->pMP3)
 		if (player->pMP3)
 		{
 		{

+ 2 - 2
palcfg.c

@@ -297,7 +297,7 @@ PAL_LoadConfig(
 
 
 	for (PALCFG_ITEM i = PALCFG_ALL_MIN; i < PALCFG_ALL_MAX; i++) values[i] = gConfigItems[i].DefaultValue;
 	for (PALCFG_ITEM i = PALCFG_ALL_MIN; i < PALCFG_ALL_MAX; i++) values[i] = gConfigItems[i].DefaultValue;
 
 
-	if (fFromFile && (fp = fopen(PAL_CombinePath(0, PAL_CONFIG_PREFIX, "sdlpal.cfg"), "r")))
+	if (fFromFile && (fp = UTIL_OpenFileAtPathForMode(PAL_CONFIG_PREFIX, "sdlpal.cfg", "r")))
 	{
 	{
 		PAL_LARGE char buf[512];
 		PAL_LARGE char buf[512];
 
 
@@ -477,7 +477,7 @@ PAL_SaveConfig(
 )
 )
 {
 {
 	char buf[512];
 	char buf[512];
-	FILE *fp = fopen(PAL_CombinePath(0, PAL_CONFIG_PREFIX, "sdlpal.cfg"), "w");
+	FILE *fp = UTIL_OpenFileAtPathForMode(PAL_CONFIG_PREFIX, "sdlpal.cfg", "w");
 
 
 	if (fp)
 	if (fp)
 	{
 	{

+ 2 - 0
rixplay.cpp

@@ -386,6 +386,8 @@ RIX_Init(
     0 if success, -1 if cannot allocate memory, -2 if file not found.
     0 if success, -1 if cannot allocate memory, -2 if file not found.
 --*/
 --*/
 {
 {
+	if (!szFileName) return NULL;
+
 	LPRIXPLAYER pRixPlayer = new RIXPLAYER;
 	LPRIXPLAYER pRixPlayer = new RIXPLAYER;
 	if (pRixPlayer == NULL)
 	if (pRixPlayer == NULL)
 	{
 	{

+ 1 - 1
ui.c

@@ -857,7 +857,7 @@ PAL_LoadObjectDesc(
    LPOBJECTDESC               lpDesc = NULL, pNew = NULL;
    LPOBJECTDESC               lpDesc = NULL, pNew = NULL;
    unsigned int               i;
    unsigned int               i;
 
 
-   fp = fopen(lpszFileName, "r");
+   fp = UTIL_OpenFileForMode(lpszFileName, "r");
 
 
    if (fp == NULL)
    if (fp == NULL)
    {
    {

+ 2 - 2
uigame.c

@@ -24,7 +24,7 @@
 
 
 static WORD GetSavedTimes(int iSaveSlot)
 static WORD GetSavedTimes(int iSaveSlot)
 {
 {
-	FILE *fp = fopen(PAL_CombinePath(0, gConfig.pszSavePath, PAL_va(1, "%d.rpg", iSaveSlot)), "rb");
+	FILE *fp = UTIL_OpenFileAtPath(gConfig.pszSavePath, PAL_va(0, "%d.rpg", iSaveSlot));
 	WORD wSavedTimes = 0;
 	WORD wSavedTimes = 0;
 	if (fp != NULL)
 	if (fp != NULL)
 	{
 	{
@@ -599,7 +599,7 @@ PAL_SystemMenu(
                wSavedTimes = curSavedTimes;
                wSavedTimes = curSavedTimes;
             }
             }
          }
          }
-         PAL_SaveGame(PAL_CombinePath(0, gConfig.pszSavePath, PAL_va(1, "%d.rpg", iSlot)), wSavedTimes + 1);
+         PAL_SaveGame(iSlot, wSavedTimes + 1);
       }
       }
       break;
       break;
 
 

+ 108 - 28
util.c

@@ -33,6 +33,13 @@
 #endif
 #endif
 
 
 static char internal_buffer[PAL_MAX_GLOBAL_BUFFERS + 1][PAL_GLOBAL_BUFFER_SIZE];
 static char internal_buffer[PAL_MAX_GLOBAL_BUFFERS + 1][PAL_GLOBAL_BUFFER_SIZE];
+#ifdef _WIN32
+static const char * const path_separators = "/\\";
+# define isseparator(x) ((x) == '/' || (x) == '\\')
+#else
+static const char * const path_separators = "/";
+# define isseparator(x) ((x) == '/')
+#endif
 
 
 void UTIL_MsgBox(char *string)
 void UTIL_MsgBox(char *string)
 {
 {
@@ -483,32 +490,47 @@ UTIL_OpenFileForMode(
 
 
 --*/
 --*/
 {
 {
-	FILE         *fp;
-
+	//
+	// If lpszFileName is an absolute path, use its last element as filename
+	//
 	if (UTIL_IsAbsolutePath(lpszFileName))
 	if (UTIL_IsAbsolutePath(lpszFileName))
-		fp = fopen(lpszFileName, szMode);
-	else
-		fp = fopen(UTIL_CombinePath(internal_buffer[PAL_MAX_GLOBAL_BUFFERS], PAL_GLOBAL_BUFFER_SIZE, 2, gConfig.pszGamePath, lpszFileName), szMode);
-
-#if !defined(PAL_FILESYSTEM_IGNORE_CASE) || !PAL_FILESYSTEM_IGNORE_CASE
-	if (fp == NULL)
 	{
 	{
-		//
-		// try to find the matching file in the directory.
-		//
-		struct dirent **list;
-		int n = scandir(gConfig.pszGamePath, &list, 0, alphasort);
-		while (n-- > 0)
+		char *temp = strdup(lpszFileName), *filename = temp;
+		FILE *fp = NULL;
+		for (char *next = strpbrk(filename, path_separators); next; next = strpbrk(filename = next + 1, path_separators));
+		if (*filename)
 		{
 		{
-			if (!fp && strcasecmp(list[n]->d_name, lpszFileName) == 0)
-				fp = fopen(UTIL_CombinePath(internal_buffer[PAL_MAX_GLOBAL_BUFFERS], PAL_GLOBAL_BUFFER_SIZE, 2, gConfig.pszGamePath, list[n]->d_name), szMode);
-			free(list[n]);
+			filename[-1] = '\0';
+			fp = UTIL_OpenFileAtPathForMode(*temp ? temp : "/", filename, szMode);
 		}
 		}
-		free(list);
+		free(temp);
+		return fp;
 	}
 	}
-#endif
 
 
-	return fp;
+	return UTIL_OpenFileAtPathForMode(gConfig.pszGamePath, lpszFileName, szMode);
+}
+
+FILE *
+UTIL_OpenFileAtPath(
+	LPCSTR              lpszPath,
+	LPCSTR              lpszFileName
+)
+{
+	return UTIL_OpenFileAtPathForMode(lpszPath, lpszFileName, "rb");
+}
+
+FILE *
+UTIL_OpenFileAtPathForMode(
+	LPCSTR              lpszPath,
+	LPCSTR              lpszFileName,
+	LPCSTR              szMode
+)
+{
+	//
+	// Construct full path according to lpszPath and lpszFileName
+	//
+	const char *path = UTIL_GetFullPathName(internal_buffer[PAL_MAX_GLOBAL_BUFFERS], PAL_GLOBAL_BUFFER_SIZE, lpszPath, lpszFileName);
+	return path ? fopen(path, szMode) : NULL;
 }
 }
 
 
 VOID
 VOID
@@ -537,19 +559,78 @@ UTIL_CloseFile(
 }
 }
 
 
 
 
+const char *
+UTIL_GetFullPathName(
+	char       *buffer,
+	size_t      buflen,
+	const char *basepath,
+	const char *subpath
+)
+{
+	if (!buffer || !basepath || !subpath || buflen == 0) return NULL;
+
+	int baselen = strlen(basepath), sublen = strlen(subpath);
+	if (sublen == 0) return NULL;
+
+	char *_base = strdup(basepath), *_sub = strdup(subpath);
+	const char *result = NULL;
+
+	if (access(UTIL_CombinePath(internal_buffer[PAL_MAX_GLOBAL_BUFFERS], PAL_GLOBAL_BUFFER_SIZE, 2, _base, _sub), 0) == 0)
+	{
+		result = internal_buffer[PAL_MAX_GLOBAL_BUFFERS];
+	}
+
+#if !defined(PAL_FILESYSTEM_IGNORE_CASE) || !PAL_FILESYSTEM_IGNORE_CASE
+	if (result == NULL)
+	{
+		size_t pos = strspn(_sub, path_separators);
+
+		if (pos < sublen)
+		{
+			char *start = _sub + pos;
+			char *end = strpbrk(start, path_separators);
+			if (end) *end = '\0';
+
+			//
+			// try to find the matching file in the directory.
+			//
+			struct dirent **list;
+			int n = scandir(_base, &list, 0, alphasort);
+			while (n-- > 0)
+			{
+				if (!result && strcasecmp(list[n]->d_name, start) == 0)
+				{
+					result = UTIL_CombinePath(internal_buffer[PAL_MAX_GLOBAL_BUFFERS], PAL_GLOBAL_BUFFER_SIZE, 2, _base, list[n]->d_name);
+					if (end)
+						result = UTIL_GetFullPathName(internal_buffer[PAL_MAX_GLOBAL_BUFFERS], PAL_GLOBAL_BUFFER_SIZE, result, end + 1);
+					else if (access(result, 0) != 0)
+						result = NULL;
+				}
+				free(list[n]);
+			}
+			free(list);
+		}
+	}
+#endif
+	if (result != NULL)
+	{
+		result = (char *)memmove(buffer, result, min(buflen, strlen(result)));
+	}
+
+	free(_base);
+	free(_sub);
+
+	return result;
+}
+
 const char *
 const char *
 UTIL_CombinePath(
 UTIL_CombinePath(
 	char       *buffer,
 	char       *buffer,
-	int         buflen,
+	size_t      buflen,
 	int         numentry,
 	int         numentry,
 	...
 	...
 )
 )
 {
 {
-#ifdef _WIN32
-#define isseparator(x) ((x) == '/' || (x) == '\\')
-#else
-#define isseparator(x) ((x) == '/')
-#endif
 	if (buffer && buflen > 0 && numentry > 0)
 	if (buffer && buflen > 0 && numentry > 0)
 	{
 	{
 		const char *retval = buffer;
 		const char *retval = buffer;
@@ -562,7 +643,7 @@ UTIL_CombinePath(
 			int path_len = path ? strlen(path) : 0;
 			int path_len = path ? strlen(path) : 0;
 			int append_delim = (i < numentry - 1 && path_len > 0 && !isseparator(path[path_len - 1]));
 			int append_delim = (i < numentry - 1 && path_len > 0 && !isseparator(path[path_len - 1]));
 			
 			
-			for (int is_sep = 0, j = 0; j < path_len && buflen > append_delim + 1; j++)
+			for (int is_sep = 0, j = 0; j < path_len && buflen > (size_t)append_delim + 1; j++)
 			{
 			{
 				//
 				//
 				// Skip continuous path separators
 				// Skip continuous path separators
@@ -600,7 +681,6 @@ UTIL_CombinePath(
 	{
 	{
 		return NULL;
 		return NULL;
 	}
 	}
-#undef isseparator
 }
 }
 
 
 
 

+ 40 - 1
util.h

@@ -46,6 +46,7 @@ char *
 UTIL_GlobalBuffer(
 UTIL_GlobalBuffer(
 	int         index
 	int         index
 );
 );
+#define PAL_BUFFER_SIZE_ARGS(i) UTIL_GlobalBuffer(i), PAL_GLOBAL_BUFFER_SIZE
 
 
 /*++
 /*++
   Purpose:
   Purpose:
@@ -129,6 +130,36 @@ UTIL_OpenFileForMode(
    LPCSTR               szMode
    LPCSTR               szMode
 );
 );
 
 
+FILE *
+UTIL_OpenFileAtPath(
+	LPCSTR              lpszPath,
+	LPCSTR              lpszFileName
+);
+
+/*++
+  Purpose:
+
+    Open a file in desired mode at the specific path.
+	If fails, return NULL.
+
+  Parameters:
+
+    [IN]  lpszPath - path to locate the file.
+    [IN]  lpszFileName - file name to open.
+    [IN]  szMode - file open mode.
+
+  Return value:
+
+    Pointer to the file.
+
+--*/
+FILE *
+UTIL_OpenFileAtPathForMode(
+	LPCSTR              lpszPath,
+	LPCSTR              lpszFileName,
+	LPCSTR              szMode
+);
+
 VOID
 VOID
 UTIL_CloseFile(
 UTIL_CloseFile(
    FILE                *fp
    FILE                *fp
@@ -156,12 +187,20 @@ UTIL_CloseFile(
 const char *
 const char *
 UTIL_CombinePath(
 UTIL_CombinePath(
 	char       *buffer,
 	char       *buffer,
-	int         buflen,
+	size_t      buflen,
 	int         numentry,
 	int         numentry,
 	...
 	...
 );
 );
 #define PAL_CombinePath(i, d, f) UTIL_CombinePath(UTIL_GlobalBuffer(i), PAL_GLOBAL_BUFFER_SIZE, 2, (d), (f))
 #define PAL_CombinePath(i, d, f) UTIL_CombinePath(UTIL_GlobalBuffer(i), PAL_GLOBAL_BUFFER_SIZE, 2, (d), (f))
 
 
+const char *
+UTIL_GetFullPathName(
+	char       *buffer,
+	size_t      buflen,
+	const char *basepath,
+	const char *subpath
+);
+
 /*
 /*
  * Platform-specific utilities
  * Platform-specific utilities
  */
  */