diff --git a/CMakeLists.txt b/CMakeLists.txt index daa6f05..a652fe0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,6 +125,13 @@ endif() install(TARGETS vgm_ptch RUNTIME DESTINATION "bin") +add_executable(vgm_ren + vgm_ren.c +) +target_link_libraries(vgm_ptch ${ZLIB_LIBRARIES}) +install(TARGETS vgm_ptch RUNTIME DESTINATION "bin") + + add_executable(vgm_smp1 vgm_smp1.c ) diff --git a/README.md b/README.md index df04d25..cea5a09 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ by Valley Bell (@valleybell) [vgm_mono - VGM Mono](#vgm-mono-vgm_mono) [vgm_ndlz - VGM Undualizer](#vgm-undualizer-vgm_ndlz) [vgm_ptch - VGM Patcher](#vgm-patcher-vgm_ptch) +[vgm_ren.c - VGM Renamer](#vgm-renamer-vgm_ren) [vgm_smp1 - Remove 1 Sample Delays](#remove-1-sample-delays-vgm_smp1) [vgm_sptd - VGM Splitter (Delay Edition)](#vgm-splitter-delay-edition-vgm_sptd) [vgm_spts - VGM Splitter (Sample Edition)](#vgm-splitter-sample-edition-vgm_spts) @@ -235,6 +236,13 @@ general VGM patching utility, allows editing the VGM header (chip clocks/chip se TODO: better description +### VGM Renamer (vgm_ren) + +This tool renames a VGM so that its file name follows the VGM's title tag. + +When a playlist is given, track numbers are added and the playlist is rewritten with the new file names. + + ### Remove 1 Sample Delays (vgm_smp1) This tool helps to reduce the size of VGMs by removing delays of 1 sample length. diff --git a/makefile b/makefile index 62ec9c6..7bf593f 100644 --- a/makefile +++ b/makefile @@ -4,6 +4,8 @@ # ######################## +# WARNING: Heavily outdated. Please use the CMake project instead. + # TODO: # - link dro2vgm/imf2vgm/raw2vgm/vgm_vol without -lz # - link -lm only to vgm_cnt, vgm_ptch, vgm_vol, vgm2txt diff --git a/vc6/vgm_ren.dsp b/vc6/vgm_ren.dsp new file mode 100644 index 0000000..0c660b1 --- /dev/null +++ b/vc6/vgm_ren.dsp @@ -0,0 +1,112 @@ +# Microsoft Developer Studio Project File - Name="vgm_ren" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** NICHT BEARBEITEN ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=vgm_ren - Win32 Debug +!MESSAGE Dies ist kein gültiges Makefile. Zum Erstellen dieses Projekts mit NMAKE +!MESSAGE verwenden Sie den Befehl "Makefile exportieren" und führen Sie den Befehl +!MESSAGE +!MESSAGE NMAKE /f "vgm_ren.mak". +!MESSAGE +!MESSAGE Sie können beim Ausführen von NMAKE eine Konfiguration angeben +!MESSAGE durch Definieren des Makros CFG in der Befehlszeile. Zum Beispiel: +!MESSAGE +!MESSAGE NMAKE /f "vgm_ren.mak" CFG="vgm_ren - Win32 Debug" +!MESSAGE +!MESSAGE Für die Konfiguration stehen zur Auswahl: +!MESSAGE +!MESSAGE "vgm_ren - Win32 Release" (basierend auf "Win32 (x86) Console Application") +!MESSAGE "vgm_ren - Win32 Debug" (basierend auf "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "vgm_ren - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release\intermediate" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\zlib" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 msvcrt.lib msvcprt.lib kernel32.lib user32.lib zdll.lib /nologo /subsystem:console /pdb:none /machine:I386 /nodefaultlib /libpath:"..\zlib" + +!ELSEIF "$(CFG)" == "vgm_ren - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "vgm_ren___Win32_Debug" +# PROP BASE Intermediate_Dir "vgm_ren___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug\intermediate" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\zlib" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 msvcrtd.lib msvcprtd.lib kernel32.lib user32.lib zdll.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib /pdbtype:sept /libpath:"..\zlib" + +!ENDIF + +# Begin Target + +# Name "vgm_ren - Win32 Release" +# Name "vgm_ren - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\vgm_ren.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\common.h +# End Source File +# Begin Source File + +SOURCE=..\VGMFile.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/vc6/vgmtools.dsw b/vc6/vgmtools.dsw index c4a1720..ed58203 100644 --- a/vc6/vgmtools.dsw +++ b/vc6/vgmtools.dsw @@ -1,5 +1,5 @@ Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! +# WARNUNG: DIESE ARBEITSBEREICHSDATEI DARF NICHT BEARBEITET ODER GELÖSCHT WERDEN! ############################################################################### @@ -291,6 +291,18 @@ Package=<4> ############################################################################### +Project: "vgm_ren"=".\vgm_ren.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + Project: "vgm_trim"=".\vgm_trim.dsp" - Package Owner=<4> Package=<5> diff --git a/vgm_ren.c b/vgm_ren.c new file mode 100644 index 0000000..3979528 --- /dev/null +++ b/vgm_ren.c @@ -0,0 +1,500 @@ +// vgm_ren.c - VGM Renamer +// + +#include +#include +#include +#include // for isalnum() +#include +#include // for setlocale() +#include + +#include "stdtype.h" +#include "stdbool.h" +#include "VGMFile.h" +#include "common.h" + + +static const char* GetFTitle(const char* filePath); // GetFileTitle is already used by the Windows API +static bool OpenVGMFile(const char* FileName); +static wchar_t* ReadWStrFromFile(gzFile hFile, UINT32* FilePos, UINT32 EOFPos); +static void ReadPlaylist(const char* FileName); +static void RenameFiles(void); +static void WritePlaylist(const char* fileName); + + +typedef struct track_list +{ + char* PathSrc; + char* PathDst; + char* Title; + bool Compressed; +} TRACK_LIST; + + +UINT32 VGMDataLen; +char FilePath[MAX_PATH]; +UINT32 TrackCount; +UINT32 TrkCntDigits; +UINT32 TrackAlloc; +TRACK_LIST* TrackList; +bool IsPlayList; + +int main(int argc, char* argv[]) +{ + int ErrVal; + char FileName[MAX_PATH]; + char* FileExt; + bool PLMode; + UINT32 TempLng; + + setlocale(LC_CTYPE, ""); // set to use system codepage + + printf("VGM Renamer\n-----------\n\n"); + + ErrVal = 0; + printf("VGM or PlayList:\t"); + if (argc <= 0x01) + { + ReadFilename(FileName, sizeof(FileName)); + } + else + { + strcpy(FileName, argv[0x01]); + printf("%s\n", FileName); + } + if (! strlen(FileName)) + return 0; + + PLMode = false; + FileExt = strrchr(GetFTitle(FileName), '.'); + if (FileExt != NULL) + { + FileExt ++; + if (! stricmp(FileExt, "m3u")) + PLMode = true; + } + + TrackCount = 0; + TrackAlloc = 0; + TrackList = NULL; + IsPlayList = PLMode; + if (PLMode) + { + ReadPlaylist(FileName); + } + else + { + if (! OpenVGMFile(FileName)) + printf("Error opening the file!\n"); + } + + TrkCntDigits = 0; + if (PLMode) + { + TempLng = TrackCount; + do + { + TempLng /= 10; + TrkCntDigits ++; + } while(TempLng); + if (TrkCntDigits < 2) + TrkCntDigits = 2; + } + + printf("\n"); + RenameFiles(); + if (PLMode) + { + printf("Writing playlist ...\n"); + WritePlaylist(FileName); + } + printf("Done.\n"); + + { + UINT32 curFile; + for (curFile = 0; curFile < TrackCount; curFile ++) + { + TRACK_LIST* tlEntry = &TrackList[curFile]; + free(tlEntry->PathSrc); + free(tlEntry->PathDst); + free(tlEntry->Title); + } + free(TrackList); + } + + DblClickWait(argv[0]); + + return ErrVal; +} + +static const char* GetFTitle(const char* filePath) +{ + const char* dirSepPos; + const char* sepPos1 = strrchr(filePath, '/'); + const char* sepPos2 = strrchr(filePath, '\\'); + + if (sepPos1 == NULL) + dirSepPos = sepPos2; + else if (sepPos2 == NULL) + dirSepPos = sepPos1; + else + dirSepPos = (sepPos1 < sepPos2) ? sepPos2 : sepPos1; + + return (dirSepPos != NULL) ? &dirSepPos[1] : filePath; +} + +static bool OpenVGMFile(const char* FileName) +{ + gzFile hFile; + UINT32 fccHeader; + UINT32 CurPos; + UINT32 TempLng; + TRACK_LIST* CurTrkEntry; + VGM_HEADER VGMHead; + GD3_TAG VGMTag; + + hFile = gzopen(FileName, "rb"); + if (hFile == NULL) + return false; + + gzseek(hFile, 0x00, SEEK_SET); + gzread(hFile, &fccHeader, 0x04); + if (fccHeader != FCC_VGM) + goto OpenErr; + + if (gzseek(hFile, 0x00, SEEK_SET) == -1) + { + printf("gzseek Error!!\n"); + if (gzrewind(hFile) == -1) + { + printf("gzrewind Error!!\n"); + goto OpenErr; + } + } + TempLng = gztell(hFile); + if (TempLng != 0) + { + printf("gztell returns invalid offset: 0x%X\n", TempLng); + goto OpenErr; + } + gzread(hFile, &VGMHead, sizeof(VGM_HEADER)); + ZLIB_SEEKBUG_CHECK(VGMHead); + + // relative -> absolute addresses + VGMHead.lngEOFOffset += 0x00000004; + if (VGMHead.lngGD3Offset) + VGMHead.lngGD3Offset += 0x00000014; + if (! VGMHead.lngDataOffset) + VGMHead.lngDataOffset = 0x0000000C; + VGMHead.lngDataOffset += 0x00000034; + + CurPos = VGMHead.lngDataOffset; + if (VGMHead.lngVersion < 0x00000150) + CurPos = 0x40; + TempLng = sizeof(VGM_HEADER); + if (TempLng > CurPos) + TempLng -= CurPos; + else + TempLng = 0x00; + memset((UINT8*)&VGMHead + CurPos, 0x00, TempLng); + + if (! VGMHead.bytLoopModifier) + VGMHead.bytLoopModifier = 0x10; + + // Allocate Memory for Track List + if (TrackAlloc <= TrackCount) + { + TrackAlloc += 0x100; + TrackList = (TRACK_LIST*)realloc(TrackList, sizeof(TRACK_LIST) * TrackAlloc); + } + CurTrkEntry = &TrackList[TrackCount]; + TrackCount ++; + + // Read GD3 Tag + if (VGMHead.lngGD3Offset) + { + gzseek(hFile, VGMHead.lngGD3Offset, SEEK_SET); + gzread(hFile, &fccHeader, 0x04); + if (fccHeader != FCC_GD3) + VGMHead.lngGD3Offset = 0x00000000; + //goto OpenErr; + } + + if (VGMHead.lngGD3Offset) + { + CurPos = VGMHead.lngGD3Offset; + gzseek(hFile, CurPos, SEEK_SET); + gzread(hFile, &VGMTag, 0x0C); + CurPos += 0x0C; + TempLng = CurPos + VGMTag.lngTagLength; + VGMTag.strTrackNameE = ReadWStrFromFile(hFile, &CurPos, TempLng); + // I only need the English Track Title + /*VGMTag.strTrackNameJ = ReadWStrFromFile(hFile, &CurPos, TempLng); + VGMTag.strGameNameE = ReadWStrFromFile(hFile, &CurPos, TempLng); + VGMTag.strGameNameJ = ReadWStrFromFile(hFile, &CurPos, TempLng); + VGMTag.strSystemNameE = ReadWStrFromFile(hFile, &CurPos, TempLng); + VGMTag.strSystemNameJ = ReadWStrFromFile(hFile, &CurPos, TempLng); + VGMTag.strAuthorNameE = ReadWStrFromFile(hFile, &CurPos, TempLng); + VGMTag.strAuthorNameJ = ReadWStrFromFile(hFile, &CurPos, TempLng); + VGMTag.strReleaseDate = ReadWStrFromFile(hFile, &CurPos, TempLng); + VGMTag.strCreator = ReadWStrFromFile(hFile, &CurPos, TempLng); + VGMTag.strNotes = ReadWStrFromFile(hFile, &CurPos, TempLng);*/ + } + else + { + VGMTag.strTrackNameE = NULL; + } + + CurTrkEntry->PathSrc = strdup(FileName); + CurTrkEntry->PathDst = NULL; + CurTrkEntry->Compressed = ! gzdirect(hFile); + + TempLng = (VGMTag.strTrackNameE != NULL) ? wcslen(VGMTag.strTrackNameE) : 0; + if (TempLng) + { +#ifdef WIN32 + CurTrkEntry->Title = (char*)malloc(TempLng + 0x01); + WideCharToMultiByte(CP_ACP, 0x00, VGMTag.strTrackNameE, -1, CurTrkEntry->Title, TempLng + 0x01, "_", NULL); + CurTrkEntry->Title[TempLng] = '\0'; +#else + CurTrkEntry->Title = (char*)malloc(TempLng + 0x01); + wcstombs(CurTrkEntry->Title, VGMTag.strTrackNameE, TempLng); +#endif + } + else + { + CurTrkEntry->Title = NULL; + } + free(VGMTag.strTrackNameE); + + gzclose(hFile); + + return true; + +OpenErr: + + gzclose(hFile); + return false; +} + +static wchar_t* ReadWStrFromFile(gzFile hFile, UINT32* FilePos, UINT32 EOFPos) +{ + UINT32 CurPos; + wchar_t* TextStr; + wchar_t* TempStr; + UINT32 StrLen; + UINT16 UnicodeChr; + + // Unicode 2-Byte -> 4-Byte conversion is not neccessary, + // but it's easier to handle wchar_t than unsigned short + // (note: wchar_t is 16-bit on Windows, but 32-bit on Linux) + CurPos = *FilePos; + TextStr = (wchar_t*)malloc((EOFPos - CurPos) / 0x02 * sizeof(wchar_t)); + if (TextStr == NULL) + return NULL; + + gzseek(hFile, CurPos, SEEK_SET); + TempStr = TextStr; + StrLen = 0x00; + do + { + gzread(hFile, &UnicodeChr, 0x02); + *TempStr = (wchar_t)UnicodeChr; + TempStr ++; + CurPos += 0x02; + StrLen ++; + if (CurPos >= EOFPos) + break; + } while(*(TempStr - 1)); + + TextStr = (wchar_t*)realloc(TextStr, StrLen * sizeof(wchar_t)); + *FilePos = CurPos; + + return TextStr; +} + +static void ReadPlaylist(const char* FileName) +{ + const char M3UV2_HEAD[] = "#EXTM3U"; + const char M3UV2_META[] = "#EXTINF:"; + UINT32 METASTR_LEN; + + FILE* hFile; + UINT32 LineNo; + bool IsV2Fmt; + char TempStr[MAX_PATH]; + char FileVGM[MAX_PATH]; + char* RetStr; + + RetStr = (char*)GetFTitle(FileName); + if (RetStr != FileName) + { + strncpy(TempStr, FileName, RetStr - FileName); + TempStr[RetStr - FileName] = '\0'; + strcpy(FilePath, TempStr); + } + else + { + strcpy(FilePath, ""); + } + + hFile = fopen(FileName, "rt"); + if (hFile == NULL) + return; + + LineNo = 0; + IsV2Fmt = false; + METASTR_LEN = strlen(M3UV2_META); + while(! feof(hFile)) + { + RetStr = fgets(TempStr, MAX_PATH, hFile); + if (RetStr == NULL) + break; + //RetStr = strchr(TempStr, 0x0D); + //if (RetStr) + // *RetStr = '\0'; // remove NewLine-Character + RetStr = TempStr + strlen(TempStr) - 0x01; + while(*RetStr < 0x20) + { + *RetStr = '\0'; // remove NewLine-Characters + RetStr --; + } + if (! strlen(TempStr)) + continue; + + if (! LineNo) + { + if (! strcmp(TempStr, M3UV2_HEAD)) + { + IsV2Fmt = true; + LineNo ++; + continue; + } + } + if (IsV2Fmt) + { + if (! strncmp(TempStr, M3UV2_META, METASTR_LEN)) + { + // Ignore Metadata of m3u Version 2 + LineNo ++; + continue; + } + } + + strcpy(FileVGM, FilePath); + strcat(FileVGM, TempStr); + if (! OpenVGMFile(FileVGM)) + printf("%s\tError opening the file!\n", GetFTitle(TempStr)); + LineNo ++; + } + + fclose(hFile); + + return; +} + +static char* GenerateFileName(const char* title, UINT32 trackID, const char* extension) +{ + size_t extLen = strlen(extension); + size_t fnSize = TrkCntDigits + 1 + strlen(title) * 2 + extLen; + char* fn = (char*)malloc(fnSize + 1); + const char* src; + char* dst = fn; + + if (TrkCntDigits > 0) + dst += sprintf(dst, "%0*u ", TrkCntDigits, trackID); + + for (src = title; *src != '\0' && (dst - fn) < fnSize; ) + { + // replace: + // " -> ' + // ": " -> " - " + // "?" -> [remove] + // "!" -> [remove] + // "/" -> ", " + // "\" -> ", " + if (*src == '"') // " -> ' + { + src ++; + *dst = '\''; dst ++; + } + else if (*src == ':') // ":" -> " - " + { + strcpy(dst, " - "); dst += 3; + src ++; + while(*src == ' ') + src ++; + } + else if (*src == '?' || *src == '!') // remove '?' and '!' + { + src ++; + } + else if (*src == '/' || *src == '\\') // '/' and '\' -> ", " and fix whitespace padding + { + while(dst > fn && dst[-1] == ' ') + dst --; // remove whitespaces before the comma + *dst = '\''; dst ++; + src ++; + while(*src == ' ') + src ++; + } + else + { + *dst = *src; + src ++; dst ++; + } + } + while(dst > fn && dst[-1] == '.') + dst --; // remove dots before the extension + + if (dst > fn + fnSize - extLen) + dst = fn + fnSize - extLen; + strcpy(dst, extension); + + return fn; +} + +static void RenameFiles(void) +{ + UINT32 curFile; + + for (curFile = 0; curFile < TrackCount; curFile ++) + { + TRACK_LIST* tlEntry = &TrackList[curFile]; + const char* fileExt = tlEntry->Compressed ? ".vgz" : ".vgm"; + const char* fileTitle = GetFTitle(tlEntry->PathSrc); + size_t baseLen = fileTitle - tlEntry->PathSrc; + size_t ftSize; + + if (tlEntry->Title != NULL) + tlEntry->PathDst = GenerateFileName(tlEntry->Title, 1 + curFile, fileExt); + else + tlEntry->PathDst = GenerateFileName(fileTitle, 1 + curFile, ""); + ftSize = strlen(tlEntry->PathDst) + 1; + + tlEntry->PathDst = (char*)realloc(tlEntry->PathDst, baseLen + ftSize); + memmove(&tlEntry->PathDst[baseLen], tlEntry->PathDst, ftSize); + memcpy(tlEntry->PathDst, tlEntry->PathSrc, baseLen); + + printf("%s -> %s\n", fileTitle, GetFTitle(tlEntry->PathDst)); + rename(tlEntry->PathSrc, tlEntry->PathDst); + } + + return; +} + +static void WritePlaylist(const char* fileName) +{ + FILE* hFile; + UINT32 curFile; + + hFile = fopen(fileName, "wt"); + + for (curFile = 0; curFile < TrackCount; curFile ++) + fprintf(hFile, "%s\n", GetFTitle(TrackList[curFile].PathDst)); + + fclose(hFile); + + return; +} diff --git a/vgm_tt.cpp b/vgm_tt.cpp index ee463c4..a1a09cf 100644 --- a/vgm_tt.cpp +++ b/vgm_tt.cpp @@ -200,21 +200,21 @@ int main(int argc, char* argv[]) static bool IsDirectory(const std::string& dirName) { #ifdef _WIN32 - struct _stat fInfo; - int result = _stat(dirName.c_str(), &fInfo); - if (result) - return false; - if (fInfo.st_mode & _S_IFDIR) - return true; + struct _stat fInfo; + int result = _stat(dirName.c_str(), &fInfo); + if (result) + return false; + if (fInfo.st_mode & _S_IFDIR) + return true; #else - struct stat fInfo; - int result = stat(dirName.c_str(), &fInfo); - if (result) - return false; - if (S_ISDIR(fInfo.st_mode)) - return true; + struct stat fInfo; + int result = stat(dirName.c_str(), &fInfo); + if (result) + return false; + if (S_ISDIR(fInfo.st_mode)) + return true; #endif - return false; + return false; } static std::string Dir2Path(const std::string& dirName)