diff options
Diffstat (limited to 'docs/release/src/osd/winui/treeview.cpp')
-rw-r--r-- | docs/release/src/osd/winui/treeview.cpp | 2513 |
1 files changed, 2513 insertions, 0 deletions
diff --git a/docs/release/src/osd/winui/treeview.cpp b/docs/release/src/osd/winui/treeview.cpp new file mode 100644 index 00000000000..91a7d74c91e --- /dev/null +++ b/docs/release/src/osd/winui/treeview.cpp @@ -0,0 +1,2513 @@ +// For licensing and usage information, read docs/winui_license.txt +//**************************************************************************** + +/*************************************************************************** + + treeview.c + + TreeView support routines - MSH 11/19/1998 + +***************************************************************************/ + +// standard windows headers +#include <windows.h> +#include <windowsx.h> +#include <shellapi.h> +#include <commctrl.h> + +// standard C headers +#include <stdio.h> // for sprintf +#include <stdlib.h> // For malloc and free +#include <ctype.h> // For tolower +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> + +#ifdef _MSC_VER +#include <direct.h> +#endif +#include <tchar.h> +#include <io.h> + +// MAME/MAMEUI headers +#include "emu.h" +#include "hash.h" +#include "mui_util.h" +#include "bitmask.h" +#include "winui.h" +#include "treeview.h" +#include "resource.h" +#include "mui_opts.h" +#include "dialogs.h" +#include "winutf8.h" +#include "strconv.h" +#include "drivenum.h" +#include "screen.h" + +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#endif + +#define MAX_EXTRA_FOLDERS 256 + +/*************************************************************************** + public structures + ***************************************************************************/ + +#define ICON_MAX ARRAY_LENGTH(treeIconNames) + +/* Name used for user-defined custom icons */ +/* external *.ico file to look for. */ + +typedef struct +{ + int nResourceID; + LPCSTR lpName; +} TREEICON; + +static TREEICON treeIconNames[] = +{ + { IDI_FOLDER_OPEN, "foldopen" }, + { IDI_FOLDER, "folder" }, + { IDI_FOLDER_AVAILABLE, "foldavail" }, + { IDI_FOLDER_MANUFACTURER, "foldmanu" }, + { IDI_FOLDER_UNAVAILABLE, "foldunav" }, + { IDI_FOLDER_YEAR, "foldyear" }, + { IDI_FOLDER_SOURCE, "foldsrc" }, + { IDI_FOLDER_HORIZONTAL, "horz" }, + { IDI_FOLDER_VERTICAL, "vert" }, + { IDI_MANUFACTURER, "manufact" }, + { IDI_WORKING, "working" }, + { IDI_NONWORKING, "nonwork" }, + { IDI_YEAR, "year" }, + { IDI_SOUND, "sound" }, + { IDI_CPU, "cpu" }, + { IDI_HARDDISK, "harddisk" }, + { IDI_SOURCE, "source" } +}; + +/*************************************************************************** + private variables + ***************************************************************************/ + +/* this has an entry for every folder eventually in the UI, including subfolders */ +static TREEFOLDER **treeFolders = 0; +static UINT numFolders = 0; /* Number of folder in the folder array */ +static UINT next_folder_id = MAX_FOLDERS; +static UINT folderArrayLength = 0; /* Size of the folder array */ +static LPTREEFOLDER lpCurrentFolder = nullptr; /* Currently selected folder */ +static UINT nCurrentFolder = 0; /* Current folder ID */ +static WNDPROC g_lpTreeWndProc = 0; /* for subclassing the TreeView */ +static HIMAGELIST hTreeSmall = 0; /* TreeView Image list of icons */ + +/* this only has an entry for each TOP LEVEL extra folder + SubFolders*/ +LPEXFOLDERDATA ExtraFolderData[MAX_EXTRA_FOLDERS * MAX_EXTRA_SUBFOLDERS]; +static int numExtraFolders = 0; +static int numExtraIcons = 0; +static char *ExtraFolderIcons[MAX_EXTRA_FOLDERS]; + +// built in folders and filters +static LPCFOLDERDATA g_lpFolderData; +static LPCFILTER_ITEM g_lpFilterList; + +/*************************************************************************** + private function prototypes + ***************************************************************************/ + +extern BOOL InitFolders(void); +static BOOL CreateTreeIcons(void); +static void TreeCtrlOnPaint(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +static const char* ParseManufacturer(const char *s, int *pParsedChars ); +static const char* TrimManufacturer(const char *s); +static BOOL AddFolder(LPTREEFOLDER lpFolder); +static LPTREEFOLDER NewFolder(const char *lpTitle, UINT nFolderId, int nParent, UINT nIconId, DWORD dwFlags); +static void DeleteFolder(LPTREEFOLDER lpFolder); +static LRESULT CALLBACK TreeWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +static int InitExtraFolders(void); +static void FreeExtraFolders(void); +static void SetExtraIcons(char *name, int *id); +static BOOL TryAddExtraFolderAndChildren(int parent_index); +static BOOL TrySaveExtraFolder(LPTREEFOLDER lpFolder); +static void SaveExternalFolders(int parent_index, const char *fname); + +/*************************************************************************** + public functions + ***************************************************************************/ + +static BOOL win_move_file_utf8(const char* existingfilename, const char* newfilename) +{ + TCHAR* t_existingfilename; + TCHAR* t_newfilename; + BOOL result = FALSE; + + t_existingfilename = ui_wstring_from_utf8(existingfilename); + if( !t_existingfilename ) + return result; + + t_newfilename = ui_wstring_from_utf8(newfilename); + if( !t_newfilename ) { + free(t_existingfilename); + return result; + } + + result = MoveFile(t_existingfilename, t_newfilename); + + free(t_newfilename); + free(t_existingfilename); + + return result; +} + +/************************************************************************** + * ci_strncmp - case insensitive character array compare + * + * Returns zero if the first n characters of s1 and s2 are equal, + * ignoring case. + * stolen from datafile.c + **************************************************************************/ +static int ci_strncmp (const char *s1, const char *s2, int n) +{ + int c1, c2; + + while (n) + { + if ((c1 = tolower (*s1)) != (c2 = tolower (*s2))) + return (c1 - c2); + else if (!c1) + break; + --n; + + s1++; + s2++; + } + return 0; +} + + + +/* De-allocate all folder memory */ +void FreeFolders(void) +{ + int i = 0; + + if (treeFolders != NULL) + { + if (numExtraFolders) + { + FreeExtraFolders(); + numFolders -= numExtraFolders; + } + + for (i = numFolders - 1; i >= 0; i--) + { + DeleteFolder(treeFolders[i]); + treeFolders[i] = NULL; + numFolders--; + } + free(treeFolders); + treeFolders = NULL; + } + numFolders = 0; +} + +/* Reset folder filters */ +void ResetFilters(void) +{ + int i = 0; + + if (treeFolders != 0) + { + for (i = 0; i < (int)numFolders; i++) + { + treeFolders[i]->m_dwFlags &= ~F_MASK; + } + } +} + +void InitTree(LPCFOLDERDATA lpFolderData, LPCFILTER_ITEM lpFilterList) +{ + LONG_PTR l; + + g_lpFolderData = lpFolderData; + g_lpFilterList = lpFilterList; + + InitFolders(); + + /* this will subclass the treeview (where WM_DRAWITEM gets sent for the header control) */ + l = GetWindowLongPtr(GetTreeView(), GWLP_WNDPROC); + g_lpTreeWndProc = (WNDPROC)l; + SetWindowLongPtr(GetTreeView(), GWLP_WNDPROC, (LONG_PTR)TreeWndProc); +} + +void SetCurrentFolder(LPTREEFOLDER lpFolder) +{ + lpCurrentFolder = (lpFolder == 0) ? treeFolders[0] : lpFolder; + nCurrentFolder = (lpCurrentFolder) ? lpCurrentFolder->m_nFolderId : 0; +} + +LPTREEFOLDER GetCurrentFolder(void) +{ + return lpCurrentFolder; +} + +UINT GetCurrentFolderID(void) +{ + return nCurrentFolder; +} + +int GetNumFolders(void) +{ + return numFolders; +} + +LPTREEFOLDER GetFolder(UINT nFolder) +{ + return (nFolder < numFolders) ? treeFolders[nFolder] : NULL; +} + +LPTREEFOLDER GetFolderByID(UINT nID) +{ + UINT i; + + for (i = 0; i < numFolders; i++) + { + if (treeFolders[i]->m_nFolderId == nID) + { + return treeFolders[i]; + } + } + + return (LPTREEFOLDER)0; +} + +void AddGame(LPTREEFOLDER lpFolder, UINT nGame) +{ + if (lpFolder) + SetBit(lpFolder->m_lpGameBits, nGame); +} + +void RemoveGame(LPTREEFOLDER lpFolder, UINT nGame) +{ + ClearBit(lpFolder->m_lpGameBits, nGame); +} + +int FindGame(LPTREEFOLDER lpFolder, int nGame) +{ + return FindBit(lpFolder->m_lpGameBits, nGame, TRUE); +} + +// Called to re-associate games with folders +void ResetWhichGamesInFolders(void) +{ + UINT i, jj, k; + BOOL b; + int nGames = driver_list::total(); + + for (i = 0; i < numFolders; i++) + { + LPTREEFOLDER lpFolder = treeFolders[i]; + // setup the games in our built-in folders + for (k = 0; g_lpFolderData[k].m_lpTitle; k++) + { + if (lpFolder->m_nFolderId == g_lpFolderData[k].m_nFolderId) + { + if (g_lpFolderData[k].m_pfnQuery || g_lpFolderData[k].m_bExpectedResult) + { + SetAllBits(lpFolder->m_lpGameBits, FALSE); + for (jj = 0; jj < nGames; jj++) + { + // invoke the query function + b = g_lpFolderData[k].m_pfnQuery ? g_lpFolderData[k].m_pfnQuery(jj) : TRUE; + + // if we expect FALSE, flip the result + if (!g_lpFolderData[k].m_bExpectedResult) + b = !b; + + // if we like what we hear, add the game + if (b) + AddGame(lpFolder, jj); + } + } + break; + } + } + } +} + + +/* Used to build the GameList */ +BOOL GameFiltered(int nGame, DWORD dwMask) +{ + int i; + LPTREEFOLDER lpFolder = GetCurrentFolder(); + LPTREEFOLDER lpParent = nullptr; + + //Filter out the Bioses on all Folders, except for the Bios Folder + if (lpFolder != nullptr && lpFolder->m_nFolderId != FOLDER_BIOS) + { +// if( !( (driver_list::driver(nGame).flags & MACHINE_IS_BIOS_ROOT ) == 0) ) +// return TRUE; + if( driver_list::driver(nGame).name[0] == '_' ) + return TRUE; + } + // Filter games--return TRUE if the game should be HIDDEN in this view + if( GetFilterInherit() ) + { + if( lpFolder != nullptr ) + { + lpParent = GetFolder( lpFolder->m_nParent ); + if( lpParent != nullptr ) + { + /* Check the Parent Filters and inherit them on child, + * The inherited filters don't display on the custom Filter Dialog for the Child folder + * No need to promote all games to parent folder, works as is */ + dwMask |= lpParent->m_dwFlags; + } + } + } + + if (strlen(GetSearchText()) && _stricmp(GetSearchText(), SEARCH_PROMPT)) + { + if (MyStrStrI(driver_list::driver(nGame).type.fullname(),GetSearchText()) == NULL && + MyStrStrI(driver_list::driver(nGame).name,GetSearchText()) == NULL) + return TRUE; + } + /*Filter Text is already global*/ + if (MyStrStrI(driver_list::driver(nGame).type.fullname(),GetFilterText()) == NULL && + MyStrStrI(driver_list::driver(nGame).name,GetFilterText()) == NULL && + MyStrStrI(driver_list::driver(nGame).type.source(),GetFilterText()) == NULL && + MyStrStrI(driver_list::driver(nGame).manufacturer,GetFilterText()) == NULL) + { + return TRUE; + } + // Are there filters set on this folder? + if ((dwMask & F_MASK) == 0) + return FALSE; + + // Filter out clones? + if (dwMask & F_CLONES && DriverIsClone(nGame)) + return TRUE; + + for (i = 0; g_lpFilterList[i].m_dwFilterType; i++) + { + if (dwMask & g_lpFilterList[i].m_dwFilterType) + { + if (g_lpFilterList[i].m_pfnQuery(nGame) == g_lpFilterList[i].m_bExpectedResult) + return TRUE; + } + } + return FALSE; +} + +/* Get the parent of game in this view */ +BOOL GetParentFound(int nGame) // not used +{ + int nParentIndex = -1; + LPTREEFOLDER lpFolder = GetCurrentFolder(); + + if( lpFolder ) + { + nParentIndex = GetParentIndex(&driver_list::driver(nGame)); + + /* return FALSE if no parent is there in this view */ + if( nParentIndex == -1) + return FALSE; + + /* return FALSE if the folder should be HIDDEN in this view */ + if (TestBit(lpFolder->m_lpGameBits, nParentIndex) == 0) + return FALSE; + + /* return FALSE if the game should be HIDDEN in this view */ + if (GameFiltered(nParentIndex, lpFolder->m_dwFlags)) + return FALSE; + + return TRUE; + } + + return FALSE; +} + +LPCFILTER_ITEM GetFilterList(void) +{ + return g_lpFilterList; +} + +/*************************************************************************** + private functions + ***************************************************************************/ + +void CreateSourceFolders(int parent_index) +{ + int i,jj, k = 0; + int nGames = driver_list::total(); + int start_folder = numFolders; + LPTREEFOLDER lpFolder = treeFolders[parent_index]; + LPTREEFOLDER lpTemp; + + // no games in top level folder + SetAllBits(lpFolder->m_lpGameBits,FALSE); + for (jj = 0; jj < nGames; jj++) + { + const char *s = GetDriverFilename(jj); + + if (s == NULL || s[0] == '\0') + continue; + + // look for an existant source treefolder for this game + // (likely to be the previous one, so start at the end) + for (i=numFolders-1;i>=start_folder;i--) + { + if (strcmp(treeFolders[i]->m_lpTitle,s) == 0) + { + AddGame(treeFolders[i],jj); + break; + } + } + if (i == start_folder-1) + { + // nope, it's a source file we haven't seen before, make it. + lpTemp = NewFolder(s, next_folder_id, parent_index, IDI_SOURCE, GetFolderFlags(numFolders)); + + // Increment next_folder_id here in case code is added above + next_folder_id++; + AddFolder(lpTemp); + AddGame(lpTemp,jj); + } + } + SetNumOptionFolders(k-1); + const char *fname = "Source"; + SaveExternalFolders(parent_index, fname); +} + +void CreateScreenFolders(int parent_index) +{ + int i, jj, k = 0; + int nGames = driver_list::total(); + int start_folder = numFolders; + LPTREEFOLDER lpFolder = treeFolders[parent_index]; + LPTREEFOLDER lpTemp; + + // no games in top level folder + SetAllBits(lpFolder->m_lpGameBits,FALSE); + for (jj = 0; jj < nGames; jj++) + { + int screens = DriverNumScreens(jj); + char s[2]; + itoa(screens, s, 10); + + // look for an existant screens treefolder for this game + // (likely to be the previous one, so start at the end) + for (i=numFolders-1;i>=start_folder;i--) + { + if (strcmp(treeFolders[i]->m_lpTitle,s) == 0) + { + AddGame(treeFolders[i],jj); + break; + } + } + if (i == start_folder-1) + { + // nope, it's a screen file we haven't seen before, make it. + lpTemp = NewFolder(s, next_folder_id, parent_index, IDI_FOLDER, GetFolderFlags(numFolders)); + + // Increment next_folder_id here in case code is added above + next_folder_id++; + AddFolder(lpTemp); + AddGame(lpTemp,jj); + } + } + SetNumOptionFolders(k-1); + const char *fname = "Screen"; + SaveExternalFolders(parent_index, fname); +} + + +void CreateManufacturerFolders(int parent_index) +{ + int i, jj; + int nGames = driver_list::total(); + int start_folder = numFolders; + LPTREEFOLDER lpFolder = treeFolders[parent_index]; + LPTREEFOLDER lpTemp; + + // no games in top level folder + SetAllBits(lpFolder->m_lpGameBits,FALSE); + + for (jj = 0; jj < nGames; jj++) + { + const char *manufacturer = driver_list::driver(jj).manufacturer; + int iChars = 0; + while( manufacturer != NULL && manufacturer[0] != '\0' ) + { + const char *s = ParseManufacturer(manufacturer, &iChars); + manufacturer += iChars; + //shift to next start char + if( s != NULL && *s != 0 ) + { + const char *t = TrimManufacturer(s); + for (i=numFolders-1;i>=start_folder;i--) + { + //RS Made it case insensitive + if (ci_strncmp(treeFolders[i]->m_lpTitle,t,20) == 0 ) + { + AddGame(treeFolders[i],jj); + break; + } + } + if (i == start_folder-1) + { + // nope, it's a manufacturer we haven't seen before, make it. + lpTemp = NewFolder(t, next_folder_id, parent_index, IDI_MANUFACTURER, GetFolderFlags(numFolders)); + AddFolder(lpTemp); + AddGame(lpTemp,jj); + } + } + } + } + const char *fname = "Manufacturer"; + SaveExternalFolders(parent_index, fname); +} + +/* Make a reasonable name out of the one found in the driver array */ +static const char* ParseManufacturer(const char *s, int *pParsedChars ) +{ + static char tmp[256]; + char* ptmp; + char *t; + *pParsedChars= 0; + + if ( *s == '?' || *s == '<' || s[3] == '?' ) + { + (*pParsedChars) = strlen(s); + return "<unknown>"; + } + + ptmp = tmp; + /*if first char is a space, skip it*/ + if( *s == ' ' ) + { + (*pParsedChars)++; + ++s; + } + while( *s ) + { + /* combinations where to end string */ + + if ( + ( (*s == ' ') && ( s[1] == '(' || s[1] == '/' || s[1] == '+' ) ) || + ( *s == ']' ) || + ( *s == '/' ) || + ( *s == '?' ) + ) + { + (*pParsedChars)++; + if( s[1] == '/' || s[1] == '+' ) + (*pParsedChars)++; + break; + } + if( s[0] == ' ' && s[1] == '?' ) + { + (*pParsedChars) += 2; + s+=2; + } + + /* skip over opening braces*/ + + if ( *s != '[' ) + { + *ptmp++ = *s; + } + (*pParsedChars)++; + /*for "distributed by" and "supported by" handling*/ + if( ( (s[1] == ',') && (s[2] == ' ') && ( (s[3] == 's') || (s[3] == 'd') ) ) ) + { + //*ptmp++ = *s; + ++s; + break; + } + ++s; + } + *ptmp = '\0'; + t = tmp; + if( tmp[0] == '(' || tmp[strlen(tmp)-1] == ')' || tmp[0] == ',') + { + ptmp = strchr( tmp,'(' ); + if ( ptmp == NULL ) + { + ptmp = strchr( tmp,',' ); + if( ptmp != NULL) + { + //parse the new "supported by" and "distributed by" + ptmp++; + + if (ci_strncmp(ptmp, " supported by", 13) == 0) + { + ptmp += 13; + } + else if (ci_strncmp(ptmp, " distributed by", 15) == 0) + { + ptmp += 15; + } + else + { + return NULL; + } + } + else + { + ptmp = tmp; + if ( ptmp == NULL ) + { + return NULL; + } + } + } + if( tmp[0] == '(' || tmp[0] == ',') + { + ptmp++; + } + if (ci_strncmp(ptmp, "licensed from ", 14) == 0) + { + ptmp += 14; + } + // for the licenced from case + if (ci_strncmp(ptmp, "licenced from ", 14) == 0) + { + ptmp += 14; + } + + while ( (*ptmp != ')' ) && (*ptmp != '/' ) && *ptmp != '\0') + { + if (*ptmp == ' ' && ci_strncmp(ptmp, " license", 8) == 0) + { + break; + } + if (*ptmp == ' ' && ci_strncmp(ptmp, " licence", 8) == 0) + { + break; + } + *t++ = *ptmp++; + } + + *t = '\0'; + } + + *ptmp = '\0'; + return tmp; +} + +/* Analyze Manufacturer Names for typical patterns, that don't distinguish between companies (e.g. Co., Ltd., Inc., etc. */ +static const char* TrimManufacturer(const char *s) +{ + //Also remove Country specific suffixes (e.g. Japan, Italy, America, USA, ...) + int i=0; + char strTemp[256]; + static char strTemp2[256]; + int j=0; + int k=0; + int l = 0; + memset(strTemp, '\0', 256 ); + memset(strTemp2, '\0', 256 ); + //start analyzing from the back, as these are usually suffixes + for(i=strlen(s)-1; i>=0;i-- ) + { + + l = strlen(strTemp); + for(k=l;k>=0; k--) + strTemp[k+1] = strTemp[k]; + strTemp[0] = s[i]; + strTemp[++l] = '\0'; + switch (l) + { + case 2: + if( ci_strncmp(strTemp, "co", 2) == 0 ) + { + j=l; + while( s[strlen(s)-j-1] == ' ' || s[strlen(s)-j-1] == ',' ) + { + j++; + } + if( j!=l) + { + memset(strTemp2, '\0', 256 ); + strncpy(strTemp2, s, strlen(s)-j ); + } + } + break; + case 3: + if( ci_strncmp(strTemp, "co.", 3) == 0 || ci_strncmp(strTemp, "ltd", 3) == 0 || ci_strncmp(strTemp, "inc", 3) == 0 || ci_strncmp(strTemp, "SRL", 3) == 0 || ci_strncmp(strTemp, "USA", 3) == 0) + { + j=l; + while( s[strlen(s)-j-1] == ' ' || s[strlen(s)-j-1] == ',' ) + { + j++; + } + if( j!=l) + { + memset(strTemp2, '\0', 256 ); + strncpy(strTemp2, s, strlen(s)-j ); + } + } + break; + case 4: + if( ci_strncmp(strTemp, "inc.", 4) == 0 || ci_strncmp(strTemp, "ltd.", 4) == 0 || ci_strncmp(strTemp, "corp", 4) == 0 || ci_strncmp(strTemp, "game", 4) == 0) + { + j=l; + while( s[strlen(s)-j-1] == ' ' || s[strlen(s)-j-1] == ',' ) + { + j++; + } + if( j!=l) + { + memset(strTemp2, '\0', 256 ); + strncpy(strTemp2, s, strlen(s)-j ); + } + } + break; + case 5: + if( ci_strncmp(strTemp, "corp.", 5) == 0 || ci_strncmp(strTemp, "Games", 5) == 0 || ci_strncmp(strTemp, "Italy", 5) == 0 || ci_strncmp(strTemp, "Japan", 5) == 0) + { + j=l; + while( s[strlen(s)-j-1] == ' ' || s[strlen(s)-j-1] == ',' ) + { + j++; + } + if( j!=l) + { + memset(strTemp2, '\0', 256 ); + strncpy(strTemp2, s, strlen(s)-j ); + } + } + break; + case 6: + if( ci_strncmp(strTemp, "co-ltd", 6) == 0 || ci_strncmp(strTemp, "S.R.L.", 6) == 0) + { + j=l; + while( s[strlen(s)-j-1] == ' ' || s[strlen(s)-j-1] == ',' ) + { + j++; + } + if( j!=l) + { + memset(strTemp2, '\0', 256 ); + strncpy(strTemp2, s, strlen(s)-j ); + } + } + break; + case 7: + if( ci_strncmp(strTemp, "co. ltd", 7) == 0 || ci_strncmp(strTemp, "America", 7) == 0) + { + j=l; + while( s[strlen(s)-j-1] == ' ' || s[strlen(s)-j-1] == ',' ) + { + j++; + } + if( j!=l) + { + memset(strTemp2, '\0', 256 ); + strncpy(strTemp2, s, strlen(s)-j ); + } + } + break; + case 8: + if( ci_strncmp(strTemp, "co. ltd.", 8) == 0 ) + { + j=l; + while( s[strlen(s)-j-1] == ' ' || s[strlen(s)-j-1] == ',' ) + { + j++; + } + if( j!=l) + { + memset(strTemp2, '\0', 256 ); + strncpy(strTemp2, s, strlen(s)-j ); + } + } + break; + case 9: + if( ci_strncmp(strTemp, "co., ltd.", 9) == 0 || ci_strncmp(strTemp, "gmbh & co", 9) == 0 ) + { + j=l; + while( s[strlen(s)-j-1] == ' ' || s[strlen(s)-j-1] == ',' ) + { + j++; + } + if( j!=l) + { + memset(strTemp2, '\0', 256 ); + strncpy(strTemp2, s, strlen(s)-j ); + } + } + break; + case 10: + if( ci_strncmp(strTemp, "corp, ltd.", 10) == 0 || ci_strncmp(strTemp, "industries", 10) == 0 || ci_strncmp(strTemp, "of America", 10) == 0) + { + j=l; + while( s[strlen(s)-j-1] == ' ' || s[strlen(s)-j-1] == ',' ) + { + j++; + } + if( j!=l) + { + memset(strTemp2, '\0', 256 ); + strncpy(strTemp2, s, strlen(s)-j ); + } + } + break; + case 11: + if( ci_strncmp(strTemp, "corporation", 11) == 0 || ci_strncmp(strTemp, "enterprises", 11) == 0 ) + { + j=l; + while( s[strlen(s)-j-1] == ' ' || s[strlen(s)-j-1] == ',' ) + { + j++; + } + if( j!=l) + { + memset(strTemp2, '\0', 256 ); + strncpy(strTemp2, s, strlen(s)-j ); + } + } + break; + case 16: + if( ci_strncmp(strTemp, "industries japan", 16) == 0 ) + { + j=l; + while( s[strlen(s)-j-1] == ' ' || s[strlen(s)-j-1] == ',' ) + { + j++; + } + if( j!=l) + { + memset(strTemp2, '\0', 256 ); + strncpy(strTemp2, s, strlen(s)-j ); + } + } + break; + default: + break; + } + } + if( *strTemp2 == 0 ) + return s; + return strTemp2; +} + +void CreateCPUFolders(int parent_index) +{ + int i, j, device_folder_count = 0; + LPTREEFOLDER device_folders[512]; + LPTREEFOLDER folder; + LPTREEFOLDER lpTemp; + int nFolder = numFolders; + + for (i = 0; i < driver_list::total(); i++) + { + machine_config config(driver_list::driver(i),MameUIGlobal()); + + // enumerate through all devices + for (device_execute_interface &device : execute_interface_iterator(config.root_device())) + { + // get the name + std::string dev_name = device.device().name(); + + // do we have a folder for this device? + folder = NULL; + for (j = 0; j < device_folder_count; j++) + { + if (!strcmp(dev_name.c_str(), device_folders[j]->m_lpTitle)) + { + folder = device_folders[j]; + break; + } + } + + // are we forced to create a folder? + if (folder == NULL) + { + lpTemp = NewFolder(device.device().name(), next_folder_id, parent_index, IDI_CPU, GetFolderFlags(numFolders)); + AddFolder(lpTemp); + folder = treeFolders[nFolder++]; + + // record that we found this folder + device_folders[device_folder_count++] = folder; + } + + // cpu type #'s are one-based + AddGame(folder, i); + } + } + const char *fname = "CPU"; + SaveExternalFolders(parent_index, fname); +} + +void CreateSoundFolders(int parent_index) +{ + int i, j, device_folder_count = 0; + LPTREEFOLDER device_folders[512]; + LPTREEFOLDER folder; + LPTREEFOLDER lpTemp; + int nFolder = numFolders; + + for (i = 0; i < driver_list::total(); i++) + { + machine_config config(driver_list::driver(i),MameUIGlobal()); + + // enumerate through all devices + + for (device_sound_interface &device : sound_interface_iterator(config.root_device())) + { + // get the name + std::string dev_name = device.device().name(); + + // do we have a folder for this device? + folder = NULL; + for (j = 0; j < device_folder_count; j++) + { + if (!strcmp(dev_name.c_str(), device_folders[j]->m_lpTitle)) + { + folder = device_folders[j]; + break; + } + } + + // are we forced to create a folder? + if (folder == NULL) + { + + lpTemp = NewFolder(device.device().name(), next_folder_id, parent_index, IDI_CPU, GetFolderFlags(numFolders)); + AddFolder(lpTemp); + folder = treeFolders[nFolder++]; + + // record that we found this folder + device_folders[device_folder_count++] = folder; + } + + // cpu type #'s are one-based + AddGame(folder, i); + } + } + const char *fname = "BIOS"; + SaveExternalFolders(parent_index, fname); +} + +void CreateDeficiencyFolders(int parent_index) +{ + int jj; + int nGames = driver_list::total(); + LPTREEFOLDER lpFolder = treeFolders[parent_index]; + LPTREEFOLDER lpProt, lpWrongCol, lpImpCol, lpImpGraph, lpMissSnd, lpImpSnd, lpFlip, lpArt; + + // create our subfolders + lpProt = NewFolder("Unemulated Protection", next_folder_id, parent_index, IDI_FOLDER, GetFolderFlags(numFolders)); + lpWrongCol = NewFolder("Wrong Colors", next_folder_id, parent_index, IDI_FOLDER, GetFolderFlags(numFolders)); + lpImpCol = NewFolder("Imperfect Colors", next_folder_id, parent_index, IDI_FOLDER, GetFolderFlags(numFolders)); + lpImpGraph = NewFolder("Imperfect Graphics", next_folder_id, parent_index, IDI_FOLDER, GetFolderFlags(numFolders)); + lpMissSnd = NewFolder("Missing Sound", next_folder_id, parent_index, IDI_FOLDER, GetFolderFlags(numFolders)); + lpImpSnd = NewFolder("Imperfect Sound", next_folder_id, parent_index, IDI_FOLDER, GetFolderFlags(numFolders)); + lpFlip = NewFolder("No Cocktail", next_folder_id, parent_index, IDI_FOLDER, GetFolderFlags(numFolders)); + lpArt = NewFolder("Requires Artwork", next_folder_id, parent_index, IDI_FOLDER, GetFolderFlags(numFolders)); + AddFolder(lpProt); + AddFolder(lpWrongCol); + AddFolder(lpImpCol); + AddFolder(lpImpGraph); + AddFolder(lpMissSnd); + AddFolder(lpImpSnd); + AddFolder(lpFlip); + AddFolder(lpArt); + + // no games in top level folder + SetAllBits(lpFolder->m_lpGameBits,FALSE); + + for (jj = 0; jj < nGames; jj++) + { + uint32_t cache = GetDriverCacheLower(jj); + if (BIT(cache, 21)) + AddGame(lpWrongCol,jj); + + if (BIT(cache, 22)) + AddGame(lpProt,jj); + + if (BIT(cache, 20)) + AddGame(lpImpCol,jj); + + if (BIT(cache, 18)) + AddGame(lpImpGraph,jj); + + if (BIT(cache, 17)) + AddGame(lpMissSnd,jj); + + if (BIT(cache, 16)) + AddGame(lpImpSnd,jj); + + if (BIT(cache, 8)) + AddGame(lpFlip,jj); + + if (BIT(cache, 10)) + AddGame(lpArt,jj); + } +} + +void CreateDumpingFolders(int parent_index) +{ + int jj; + BOOL bBadDump = FALSE; + BOOL bNoDump = FALSE; + int nGames = driver_list::total(); + LPTREEFOLDER lpFolder = treeFolders[parent_index]; + LPTREEFOLDER lpBad, lpNo; + const rom_entry *region, *rom; + const game_driver *gamedrv; + + // create our two subfolders + lpBad = NewFolder("Bad Dump", next_folder_id, parent_index, IDI_FOLDER, GetFolderFlags(numFolders)); + lpNo = NewFolder("No Dump", next_folder_id, parent_index, IDI_FOLDER, GetFolderFlags(numFolders)); + AddFolder(lpBad); + AddFolder(lpNo); + + // no games in top level folder + SetAllBits(lpFolder->m_lpGameBits,FALSE); + + for (jj = 0; jj < nGames; jj++) + { + + gamedrv = &driver_list::driver(jj); + + if (!gamedrv->rom) + continue; + bBadDump = FALSE; + bNoDump = FALSE; + /* Allocate machine config */ + machine_config config(*gamedrv,MameUIGlobal()); + + for (device_t &device : device_iterator(config.root_device())) + { + for (region = rom_first_region(device); region; region = rom_next_region(region)) + { + for (rom = rom_first_file(region); rom; rom = rom_next_file(rom)) + { + if (ROMREGION_ISROMDATA(region) || ROMREGION_ISDISKDATA(region) ) + { + //name = ROM_GETNAME(rom); + util::hash_collection hashes(ROM_GETHASHDATA(rom)); + if (hashes.flag(util::hash_collection::FLAG_BAD_DUMP)) + bBadDump = TRUE; + if (hashes.flag(util::hash_collection::FLAG_NO_DUMP)) + bNoDump = TRUE; + } + } + } + } + if (bBadDump) + AddGame(lpBad,jj); + + if (bNoDump) + AddGame(lpNo,jj); + } + const char *fname = "Dumping"; + SaveExternalFolders(parent_index, fname); +} + + +void CreateYearFolders(int parent_index) +{ + int i, jj; + int nGames = driver_list::total(); + int start_folder = numFolders; + LPTREEFOLDER lpFolder = treeFolders[parent_index]; + LPTREEFOLDER lpTemp; + + // no games in top level folder + SetAllBits(lpFolder->m_lpGameBits,FALSE); + + for (jj = 0; jj < nGames; jj++) + { + char s[100]; + strcpy(s,driver_list::driver(jj).year); + + if (s[0] == '\0') + continue; + + // look for an extant year treefolder for this game + // (likely to be the previous one, so start at the end) + for (i=numFolders-1;i>=start_folder;i--) + { + if (strncmp(treeFolders[i]->m_lpTitle,s,5) == 0) + { + AddGame(treeFolders[i],jj); + break; + } + } + if (i == start_folder-1) + { + // nope, it's a year we haven't seen before, make it. + lpTemp = NewFolder(s, next_folder_id, parent_index, IDI_YEAR, GetFolderFlags(numFolders)); + AddFolder(lpTemp); + AddGame(lpTemp,jj); + } + } + const char *fname = "Year"; + SaveExternalFolders(parent_index, fname); +} + +void CreateBIOSFolders(int parent_index) +{ + int i, jj; + int nGames = driver_list::total(); + int start_folder = numFolders; + const game_driver *drv; + int nParentIndex = -1; + LPTREEFOLDER lpFolder = treeFolders[parent_index]; + LPTREEFOLDER lpTemp; + + // no games in top level folder + SetAllBits(lpFolder->m_lpGameBits,FALSE); + + for (jj = 0; jj < nGames; jj++) + { + if ( DriverIsClone(jj) ) + { + nParentIndex = GetParentIndex(&driver_list::driver(jj)); + if (nParentIndex < 0) return; + drv = &driver_list::driver(nParentIndex); + } + else + drv = &driver_list::driver(jj); + nParentIndex = GetParentIndex(drv); + + if (nParentIndex < 0 || !driver_list::driver(nParentIndex).type.fullname()) + continue; + + for (i = numFolders-1; i >= start_folder; i--) + { + if (strcmp(treeFolders[i]->m_lpTitle, driver_list::driver(nParentIndex).type.fullname()) == 0) + { + AddGame(treeFolders[i],jj); + break; + } + } + + if (i == start_folder-1) + { + lpTemp = NewFolder(driver_list::driver(nParentIndex).type.fullname(), next_folder_id++, parent_index, IDI_CPU, GetFolderFlags(numFolders)); + AddFolder(lpTemp); + AddGame(lpTemp,jj); + } + } + const char *fname = "BIOS"; + SaveExternalFolders(parent_index, fname); +} + +void CreateResolutionFolders(int parent_index) +{ + int i,jj; + int nGames = driver_list::total(); + int start_folder = numFolders; + char Resolution[20]; + LPTREEFOLDER lpFolder = treeFolders[parent_index]; + LPTREEFOLDER lpVectorV, lpVectorH; + LPTREEFOLDER lpTemp; + + // create our two subfolders + lpVectorV = NewFolder("Vector (V)", next_folder_id++, parent_index, IDI_FOLDER, GetFolderFlags(numFolders)); + lpVectorH = NewFolder("Vector (H)", next_folder_id++, parent_index, IDI_FOLDER, GetFolderFlags(numFolders)); + AddFolder(lpVectorV); + AddFolder(lpVectorH); + + // no games in top level folder + SetAllBits(lpFolder->m_lpGameBits,FALSE); + + for (jj = 0; jj < nGames; jj++) + { + machine_config config(driver_list::driver(jj), MameUIGlobal()); + const screen_device *screen = screen_device_iterator(config.root_device()).first(); + if (screen != NULL) + { + const rectangle &visarea = screen->visible_area(); + + if (DriverIsVector(jj)) + { + if (driver_list::driver(jj).flags & ORIENTATION_SWAP_XY) + { + AddGame(lpVectorV,jj); + } + else + { + AddGame(lpVectorH,jj); + } + } + else + if (driver_list::driver(jj).flags & ORIENTATION_SWAP_XY) + { + sprintf(Resolution, "%dx%d (V)", + visarea.max_y - visarea.min_y + 1, + visarea.max_x - visarea.min_x + 1); + } + else + { + sprintf(Resolution, "%dx%d (H)", + visarea.max_x - visarea.min_x + 1, + visarea.max_y - visarea.min_y + 1); + } + + for (i=numFolders-1;i>=start_folder;i--) + { + if (strcmp(treeFolders[i]->m_lpTitle, Resolution) == 0) + { + AddGame(treeFolders[i],jj); + break; + } + } + if (i == start_folder-1) + { + lpTemp = NewFolder(Resolution, next_folder_id++, parent_index, IDI_FOLDER, GetFolderFlags(numFolders)); + AddFolder(lpTemp); + AddGame(lpTemp,jj); + } + } + } + const char *fname = "Resolution"; + SaveExternalFolders(parent_index, fname); +} + +void CreateFPSFolders(int parent_index) +{ + int i, jj; + int nGames = driver_list::total(); + int nFolder = numFolders; + LPTREEFOLDER lpFolder = treeFolders[parent_index]; + LPTREEFOLDER map[256]; + LPTREEFOLDER lpTemp; + float fps[256]; + int nFPS = 0; + + // no games in top level folder + SetAllBits(lpFolder->m_lpGameBits,FALSE); + + for (i = 0; i < nGames; i++) + { + float f; + machine_config config(driver_list::driver(i), MameUIGlobal()); + const screen_device *screen = screen_device_iterator(config.root_device()).first(); + if (screen) + { + f = ATTOSECONDS_TO_HZ(screen->refresh_attoseconds()); + + for (jj = 0; jj < nFPS; jj++) + if (fps[jj] == f) + break; + + if (nFPS == jj) + { + char buf[50]; + + assert(nFPS + 1 < ARRAY_LENGTH(fps)); + assert(nFPS + 1 < ARRAY_LENGTH(map)); + + sprintf(buf, "%f Hz", f); + + lpTemp = NewFolder(buf, next_folder_id++, parent_index, IDI_FOLDER, GetFolderFlags(numFolders)); + AddFolder(lpTemp); + map[nFPS] = treeFolders[nFolder++]; + fps[nFPS++] = f; + } + + AddGame(map[jj],i); + } + } + const char *fname = "Refresh"; + SaveExternalFolders(parent_index, fname); +} + +// adds these folders to the treeview +void ResetTreeViewFolders(void) +{ + HWND hTreeView = GetTreeView(); + int i; + TVITEM tvi; + TVINSERTSTRUCT tvs; + BOOL res; + + HTREEITEM shti; // for current child branches + + // currently "cached" parent + HTREEITEM hti_parent = NULL; + int index_parent = -1; + + res = TreeView_DeleteAllItems(hTreeView); + + //dprintf("Adding folders to tree ui indices %i to %i\n",start_index,end_index); + + tvs.hInsertAfter = TVI_SORT; + + for (i=0;i<numFolders;i++) + { + LPTREEFOLDER lpFolder = treeFolders[i]; + + if (lpFolder->m_nParent == -1) + { + if (lpFolder->m_nFolderId < MAX_FOLDERS) + { + // it's a built in folder, let's see if we should show it + if (GetShowFolder(lpFolder->m_nFolderId) == FALSE) + { + continue; + } + } + + tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE; + tvs.hParent = TVI_ROOT; + tvi.pszText = lpFolder->m_lptTitle; + tvi.lParam = (LPARAM)lpFolder; + tvi.iImage = GetTreeViewIconIndex(lpFolder->m_nIconId); + tvi.iSelectedImage = 0; + + tvs.item = tvi; + + // Add root branch + hti_parent = TreeView_InsertItem(hTreeView, &tvs); + + continue; + } + + // not a top level branch, so look for parent + if (treeFolders[i]->m_nParent != index_parent) + { + + hti_parent = TreeView_GetRoot(hTreeView); + while (1) + { + if (hti_parent == NULL) + { + // couldn't find parent folder, so it's a built-in but + // not shown folder + break; + } + + tvi.hItem = hti_parent; + tvi.mask = TVIF_PARAM; + res = TreeView_GetItem(hTreeView,&tvi); + if (((LPTREEFOLDER)tvi.lParam) == treeFolders[treeFolders[i]->m_nParent]) + break; + + hti_parent = TreeView_GetNextSibling(hTreeView,hti_parent); + } + + // if parent is not shown, then don't show the child either obviously! + if (hti_parent == NULL) + continue; + + index_parent = treeFolders[i]->m_nParent; + } + + tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE; + tvs.hParent = hti_parent; + tvi.iImage = GetTreeViewIconIndex(treeFolders[i]->m_nIconId); + tvi.iSelectedImage = 0; + tvi.pszText = treeFolders[i]->m_lptTitle; + tvi.lParam = (LPARAM)treeFolders[i]; + + tvs.item = tvi; + + // Add it to this tree branch + shti = TreeView_InsertItem(hTreeView, &tvs); + + } +} + +void SelectTreeViewFolder(int folder_id) +{ + HWND hTreeView = GetTreeView(); + HTREEITEM hti; + TVITEM tvi; + BOOL res; + + memset(&tvi,0,sizeof(tvi)); + + hti = TreeView_GetRoot(hTreeView); + + while (hti != NULL) + { + HTREEITEM hti_next; + + tvi.hItem = hti; + tvi.mask = TVIF_PARAM; + res = TreeView_GetItem(hTreeView,&tvi); + + if (((LPTREEFOLDER)tvi.lParam)->m_nFolderId == folder_id) + { + res = TreeView_SelectItem(hTreeView,tvi.hItem); + SetCurrentFolder((LPTREEFOLDER)tvi.lParam); + return; + } + + hti_next = TreeView_GetChild(hTreeView,hti); + if (hti_next == NULL) + { + hti_next = TreeView_GetNextSibling(hTreeView,hti); + if (hti_next == NULL) + { + hti_next = TreeView_GetParent(hTreeView,hti); + if (hti_next != NULL) + hti_next = TreeView_GetNextSibling(hTreeView,hti_next); + } + } + hti = hti_next; + } + + // could not find folder to select + // make sure we select something + tvi.hItem = TreeView_GetRoot(hTreeView); + tvi.mask = TVIF_PARAM; + res = TreeView_GetItem(hTreeView,&tvi); + + res = TreeView_SelectItem(hTreeView,tvi.hItem); + SetCurrentFolder((LPTREEFOLDER)tvi.lParam); +} + +/* + * Does this folder have an INI associated with it? + * Currently only TRUE for FOLDER_VECTOR and children + * of FOLDER_SOURCE. + */ +static BOOL FolderHasIni(LPTREEFOLDER lpFolder) { + if (FOLDER_VECTOR == lpFolder->m_nFolderId || + FOLDER_VERTICAL == lpFolder->m_nFolderId || + FOLDER_HORIZONTAL == lpFolder->m_nFolderId) { + return TRUE; + } + if (lpFolder->m_nParent != -1 + && FOLDER_SOURCE == treeFolders[lpFolder->m_nParent]->m_nFolderId) { + return TRUE; + } + return FALSE; +} + +/* Add a folder to the list. Does not allocate */ +static BOOL AddFolder(LPTREEFOLDER lpFolder) +{ + TREEFOLDER **tmpTree = NULL; + UINT oldFolderArrayLength = folderArrayLength; + if (numFolders + 1 >= folderArrayLength) + { + folderArrayLength += 500; + tmpTree = (TREEFOLDER **)malloc(sizeof(TREEFOLDER **) * folderArrayLength); + memcpy(tmpTree,treeFolders,sizeof(TREEFOLDER **) * oldFolderArrayLength); + if (treeFolders) free(treeFolders); + treeFolders = tmpTree; + } + + /* Is there an folder.ini that can be edited? */ + if (FolderHasIni(lpFolder)) { + lpFolder->m_dwFlags |= F_INIEDIT; + } + + treeFolders[numFolders] = lpFolder; + numFolders++; + return TRUE; +} + +/* Allocate and initialize a NEW TREEFOLDER */ +static LPTREEFOLDER NewFolder(const char *lpTitle, UINT nFolderId, int nParent, UINT nIconId, DWORD dwFlags) +{ + LPTREEFOLDER lpFolder = (LPTREEFOLDER)malloc(sizeof(TREEFOLDER)); + memset(lpFolder, '\0', sizeof (TREEFOLDER)); + lpFolder->m_lpTitle = (LPSTR)malloc(strlen(lpTitle) + 1); + strcpy((char *)lpFolder->m_lpTitle,lpTitle); + lpFolder->m_lptTitle = ui_wstring_from_utf8(lpFolder->m_lpTitle); + lpFolder->m_lpGameBits = NewBits(driver_list::total()); + lpFolder->m_nFolderId = nFolderId; + lpFolder->m_nParent = nParent; + lpFolder->m_nIconId = nIconId; + lpFolder->m_dwFlags = dwFlags; + return lpFolder; +} + +/* Deallocate the passed in LPTREEFOLDER */ +static void DeleteFolder(LPTREEFOLDER lpFolder) +{ + if (lpFolder) + { + if (lpFolder->m_lpGameBits) + { + DeleteBits(lpFolder->m_lpGameBits); + lpFolder->m_lpGameBits = 0; + } + free(lpFolder->m_lptTitle); + lpFolder->m_lptTitle = 0; + free(lpFolder->m_lpTitle); + lpFolder->m_lpTitle = 0; + free(lpFolder); + lpFolder = 0; + } +} + +/* Can be called to re-initialize the array of treeFolders */ +BOOL InitFolders(void) +{ + int i = 0; + DWORD dwFolderFlags; + if (treeFolders) + { + for (i = numFolders - 1; i >= 0; i--) + { + DeleteFolder(treeFolders[i]); + treeFolders[i] = 0; + numFolders--; + } + } + numFolders = 0; + if (folderArrayLength == 0) + { + folderArrayLength = 200; + treeFolders = (TREEFOLDER **)malloc(sizeof(TREEFOLDER **) * folderArrayLength); + if (!treeFolders) + { + folderArrayLength = 0; + return 0; + } + else + memset(treeFolders,'\0', sizeof(TREEFOLDER **) * folderArrayLength); + } + // built-in top level folders + for (i = 0; g_lpFolderData[i].m_lpTitle; i++) + { + if (RequiredDriverCache() || (!RequiredDriverCache() && !g_lpFolderData[i].m_process)) + { + LPCFOLDERDATA fData = &g_lpFolderData[i]; + /* get the saved folder flags */ + dwFolderFlags = GetFolderFlags(numFolders); + /* create the folder */ + AddFolder(NewFolder(fData->m_lpTitle, fData->m_nFolderId, -1, fData->m_nIconId, dwFolderFlags)); + } + } + + numExtraFolders = InitExtraFolders(); + + for (i = 0; i < numExtraFolders; i++) + { + LPEXFOLDERDATA fExData = ExtraFolderData[i]; + // OR in the saved folder flags + dwFolderFlags = fExData->m_dwFlags | GetFolderFlags(numFolders); + // create the folder, but if we are building the cache, the name must not be a pre-built one + int k = 0; + if (RequiredDriverCache()) + for (int j = 0; g_lpFolderData[j].m_lpTitle; j++) + if (strcmp(fExData->m_szTitle, g_lpFolderData[j].m_lpTitle)==0) + k++; + + if (k == 0) + AddFolder(NewFolder(fExData->m_szTitle,fExData->m_nFolderId,fExData->m_nParent, fExData->m_nIconId,dwFolderFlags)); + } + +// creates child folders of all the top level folders, including custom ones + int num_top_level_folders = numFolders; + + for (int i = 0; i < num_top_level_folders; i++) + { + LPTREEFOLDER lpFolder = treeFolders[i]; + LPCFOLDERDATA lpFolderData = NULL; + + for (int j = 0; g_lpFolderData[j].m_lpTitle; j++) + { + if (g_lpFolderData[j].m_nFolderId == lpFolder->m_nFolderId) + { + lpFolderData = &g_lpFolderData[j]; + break; + } + } + + if (lpFolderData) + { + if (lpFolderData->m_pfnCreateFolders) + { + if (RequiredDriverCache() && lpFolderData->m_process) // rebuild cache + lpFolderData->m_pfnCreateFolders(i); + else + if (!lpFolderData->m_process) // build every time (CreateDeficiencyFolders) + lpFolderData->m_pfnCreateFolders(i); + } + } + else + { + if ((lpFolder->m_dwFlags & F_CUSTOM) == 0) + { + dprintf("Internal inconsistency with non-built-in folder, but not custom\n"); + continue; + } + + //dprintf("Loading custom folder %i %i\n",i,lpFolder->m_nFolderId); + + // load the extra folder files, which also adds children + if (TryAddExtraFolderAndChildren(i) == FALSE) + { + lpFolder->m_nFolderId = FOLDER_NONE; + } + } + } + + CreateTreeIcons(); + ResetWhichGamesInFolders(); + ResetTreeViewFolders(); + SelectTreeViewFolder(GetSavedFolderID()); + LoadFolderFlags(); + return TRUE; +} + +// create iconlist and Treeview control +static BOOL CreateTreeIcons() +{ + HICON hIcon; + INT i; + HINSTANCE hInst = GetModuleHandle(0); + + int numIcons = ICON_MAX + numExtraIcons; + hTreeSmall = ImageList_Create (16, 16, ILC_COLORDDB | ILC_MASK, numIcons, numIcons); + + //dprintf("Trying to load %i normal icons\n",ICON_MAX); + for (i = 0; i < ICON_MAX; i++) + { + hIcon = LoadIconFromFile(treeIconNames[i].lpName); + if (!hIcon) + hIcon = LoadIcon(hInst, MAKEINTRESOURCE(treeIconNames[i].nResourceID)); + + if (ImageList_AddIcon (hTreeSmall, hIcon) == -1) + { + ErrorMsg("Error creating icon on regular folder, %i %i",i,hIcon != NULL); + return FALSE; + } + } + + //dprintf("Trying to load %i extra custom-folder icons\n",numExtraIcons); + for (i = 0; i < numExtraIcons; i++) + { + if ((hIcon = LoadIconFromFile(ExtraFolderIcons[i])) == 0) + hIcon = LoadIcon (hInst, MAKEINTRESOURCE(IDI_FOLDER)); + + if (ImageList_AddIcon(hTreeSmall, hIcon) == -1) + { + ErrorMsg("Error creating icon on extra folder, %i %i",i,hIcon != NULL); + return FALSE; + } + } + + // Be sure that all the small icons were added. + if (ImageList_GetImageCount(hTreeSmall) < numIcons) + { + ErrorMsg("Error with icon list--too few images. %i %i", ImageList_GetImageCount(hTreeSmall),numIcons); + return FALSE; + } + + // Be sure that all the small icons were added. + + if (ImageList_GetImageCount (hTreeSmall) < ICON_MAX) + { + ErrorMsg("Error with icon list--too few images. %i < %i", ImageList_GetImageCount(hTreeSmall),(INT)ICON_MAX); + return FALSE; + } + + // Associate the image lists with the list view control. + (void)TreeView_SetImageList(GetTreeView(), hTreeSmall, TVSIL_NORMAL); + + return TRUE; +} + + +static void TreeCtrlOnPaint(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + PAINTSTRUCT ps; + HDC hDC; + RECT rcClip, rcClient; + HDC memDC; + HBITMAP bitmap; + HBITMAP hOldBitmap; + + HBITMAP hBackground = GetBackgroundBitmap(); + MYBITMAPINFO *bmDesc = GetBackgroundInfo(); + + hDC = BeginPaint(hWnd, &ps); + + GetClipBox(hDC, &rcClip); + GetClientRect(hWnd, &rcClient); + + // Create a compatible memory DC + memDC = CreateCompatibleDC(hDC); + + // Select a compatible bitmap into the memory DC + bitmap = CreateCompatibleBitmap(hDC, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); + hOldBitmap = (HBITMAP)SelectObject(memDC, bitmap); + + // First let the control do its default drawing. + CallWindowProc(g_lpTreeWndProc, hWnd, uMsg, (WPARAM)memDC, 0); + + // Draw bitmap in the background + + { + HPALETTE hPAL; + HDC maskDC; + HBITMAP maskBitmap; + HDC tempDC; + HDC imageDC; + HBITMAP bmpImage; + HBITMAP hOldBmpImage; + HBITMAP hOldMaskBitmap; + HBITMAP hOldHBitmap; + int i, j; + RECT rcRoot; + + // Now create a mask + maskDC = CreateCompatibleDC(hDC); + + // Create monochrome bitmap for the mask + maskBitmap = CreateBitmap(rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, 1, 1, NULL); + + hOldMaskBitmap = (HBITMAP)SelectObject(maskDC, maskBitmap); + SetBkColor(memDC, GetSysColor(COLOR_WINDOW)); + + // Create the mask from the memory DC + BitBlt(maskDC, 0, 0, rcClient.right - rcClient.left, + rcClient.bottom - rcClient.top, memDC, + rcClient.left, rcClient.top, SRCCOPY); + + tempDC = CreateCompatibleDC(hDC); + hOldHBitmap = (HBITMAP)SelectObject(tempDC, hBackground); + + imageDC = CreateCompatibleDC(hDC); + bmpImage = CreateCompatibleBitmap(hDC, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); + hOldBmpImage = (HBITMAP)SelectObject(imageDC, bmpImage); + + hPAL = GetBackgroundPalette(); + if (hPAL == NULL) + hPAL = CreateHalftonePalette(hDC); + + if (GetDeviceCaps(hDC, RASTERCAPS) & RC_PALETTE && hPAL != NULL) + { + SelectPalette(hDC, hPAL, FALSE); + RealizePalette(hDC); + SelectPalette(imageDC, hPAL, FALSE); + } + + // Get x and y offset + TreeView_GetItemRect(hWnd, TreeView_GetRoot(hWnd), &rcRoot, FALSE); + rcRoot.left = -GetScrollPos(hWnd, SB_HORZ); + + // Draw bitmap in tiled manner to imageDC + for (i = rcRoot.left; i < rcClient.right; i += bmDesc->bmWidth) + for (j = rcRoot.top; j < rcClient.bottom; j += bmDesc->bmHeight) + BitBlt(imageDC, i, j, bmDesc->bmWidth, bmDesc->bmHeight, + tempDC, 0, 0, SRCCOPY); + + // Set the background in memDC to black. Using SRCPAINT with black and any other + // color results in the other color, thus making black the transparent color + SetBkColor(memDC, RGB(0,0,0)); + SetTextColor(memDC, RGB(255,255,255)); + BitBlt(memDC, rcClip.left, rcClip.top, rcClip.right - rcClip.left, + rcClip.bottom - rcClip.top, + maskDC, rcClip.left, rcClip.top, SRCAND); + + // Set the foreground to black. See comment above. + SetBkColor(imageDC, RGB(255,255,255)); + SetTextColor(imageDC, RGB(0,0,0)); + BitBlt(imageDC, rcClip.left, rcClip.top, rcClip.right - rcClip.left, + rcClip.bottom - rcClip.top, + maskDC, rcClip.left, rcClip.top, SRCAND); + + // Combine the foreground with the background + BitBlt(imageDC, rcClip.left, rcClip.top, rcClip.right - rcClip.left, + rcClip.bottom - rcClip.top, + memDC, rcClip.left, rcClip.top, SRCPAINT); + + // Draw the final image to the screen + BitBlt(hDC, rcClip.left, rcClip.top, rcClip.right - rcClip.left, + rcClip.bottom - rcClip.top, + imageDC, rcClip.left, rcClip.top, SRCCOPY); + + SelectObject(maskDC, hOldMaskBitmap); + SelectObject(tempDC, hOldHBitmap); + SelectObject(imageDC, hOldBmpImage); + + DeleteDC(maskDC); + DeleteDC(imageDC); + DeleteDC(tempDC); + DeleteBitmap(bmpImage); + DeleteBitmap(maskBitmap); + + if (GetBackgroundPalette() == NULL) + { + DeletePalette(hPAL); + hPAL = NULL; + } + } + + SelectObject(memDC, hOldBitmap); + DeleteBitmap(bitmap); + DeleteDC(memDC); + EndPaint(hWnd, &ps); + ReleaseDC(hWnd, hDC); +} + +/* Header code - Directional Arrows */ +static LRESULT CALLBACK TreeWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (GetBackgroundBitmap() != NULL) + { + switch (uMsg) + { + case WM_MOUSEMOVE: + { + if (MouseHasBeenMoved()) + ShowCursor(TRUE); + break; + } + + case WM_KEYDOWN : + if (wParam == VK_F2) + { + if (lpCurrentFolder->m_dwFlags & F_CUSTOM) + { + TreeView_EditLabel(hWnd,TreeView_GetSelection(hWnd)); + return TRUE; + } + } + break; + + case WM_ERASEBKGND: + return TRUE; + + case WM_PAINT: + TreeCtrlOnPaint(hWnd, uMsg, wParam, lParam); + UpdateWindow(hWnd); + break; + } + } + + /* message not handled */ + return CallWindowProc(g_lpTreeWndProc, hWnd, uMsg, wParam, lParam); +} + +/* + * Filter code - should be moved to filter.c/filter.h + * Added 01/09/99 - MSH <mhaaland@hypertech.com> + */ + +/* find a FOLDERDATA by folderID */ +LPCFOLDERDATA FindFilter(DWORD folderID) +{ + int i; + + for (i = 0; g_lpFolderData[i].m_lpTitle; i++) + { + if (g_lpFolderData[i].m_nFolderId == folderID) + return &g_lpFolderData[i]; + } + return (LPFOLDERDATA) 0; +} + + +LPTREEFOLDER GetFolderByName(int nParentId, const char *pszFolderName) +{ + int i = 0, nParent; + + //First Get the Parent TreeviewItem + //Enumerate Children + for(i = 0; i < numFolders/* ||treeFolders[i] != NULL*/; i++) + { + if (!strcmp(treeFolders[i]->m_lpTitle, pszFolderName)) + { + nParent = treeFolders[i]->m_nParent; + if ((nParent >= 0) && treeFolders[nParent]->m_nFolderId == nParentId) + return treeFolders[i]; + } + } + return NULL; +} + +static int InitExtraFolders(void) +{ + struct stat stat_buffer; + struct _finddata_t files; + int i, count = 0; + char* ext; + char buf[256]; + char curdir[MAX_PATH]; + const std::string t = GetFolderDir(); + const char *dir = t.c_str(); + memset(ExtraFolderData, 0, (MAX_EXTRA_FOLDERS * MAX_EXTRA_SUBFOLDERS)* sizeof(LPEXFOLDERDATA)); + + /* NPW 9-Feb-2003 - MSVC stat() doesn't like stat() called with an empty string */ + if (!dir) + dir = "."; + + // Why create the directory if it doesn't exist, just return 0 folders. + if (stat(dir, &stat_buffer) != 0) + return 0; // _mkdir(dir); + + _getcwd(curdir, MAX_PATH); + + chdir(dir); + + long hLong = _findfirst("*", &files); + + for (i = 0; i < MAX_EXTRA_FOLDERS; i++) + ExtraFolderIcons[i] = NULL; + + numExtraIcons = 0; + + while (!_findnext(hLong, &files)) + { + if ((files.attrib & _A_SUBDIR) == 0) + { + FILE *fp; + + fp = fopen(files.name, "r"); + if (fp != NULL) + { + int icon[2] = { 0, 0 }; + char *p, *name; + + while (fgets(buf, 256, fp)) + { + if (buf[0] == '[') + { + p = strchr(buf, ']'); + if (p == NULL) + continue; + + *p = '\0'; + name = &buf[1]; + if (!strcmp(name, "FOLDER_SETTINGS")) + { + while (fgets(buf, 256, fp)) + { + name = strtok(buf, " =\r\n"); + if (name == NULL) + break; + + if (!strcmp(name, "RootFolderIcon")) + { + name = strtok(NULL, " =\r\n"); + if (name != NULL) + SetExtraIcons(name, &icon[0]); + } + if (!strcmp(name, "SubFolderIcon")) + { + name = strtok(NULL, " =\r\n"); + if (name != NULL) + SetExtraIcons(name, &icon[1]); + } + } + break; + } + } + } + fclose(fp); + + strcpy(buf, files.name); + ext = strrchr(buf, '.'); + + if (ext && *(ext + 1) && !core_stricmp(ext + 1, "ini")) + { + ExtraFolderData[count] =(EXFOLDERDATA*) malloc(sizeof(EXFOLDERDATA)); + if (ExtraFolderData[count]) + { + *ext = '\0'; + + memset(ExtraFolderData[count], 0, sizeof(EXFOLDERDATA)); + + strncpy(ExtraFolderData[count]->m_szTitle, buf, 63); + ExtraFolderData[count]->m_nFolderId = next_folder_id++; + ExtraFolderData[count]->m_nParent = -1; + ExtraFolderData[count]->m_dwFlags = F_CUSTOM; + ExtraFolderData[count]->m_nIconId = icon[0] ? -icon[0] : IDI_FOLDER; + ExtraFolderData[count]->m_nSubIconId = icon[1] ? -icon[1] : IDI_FOLDER; + //dprintf("extra folder with icon %i, subicon %i\n", + //ExtraFolderData[count]->m_nIconId, + //ExtraFolderData[count]->m_nSubIconId); + count++; + } + } + } + } + } + + chdir(curdir); + return count; +} + +void FreeExtraFolders(void) +{ + int i; + + for (i = 0; i < numExtraFolders; i++) + { + if (ExtraFolderData[i]) + { + free(ExtraFolderData[i]); + ExtraFolderData[i] = NULL; + } + } + + for (i = 0; i < numExtraIcons; i++) + { + free(ExtraFolderIcons[i]); + } + + numExtraIcons = 0; + +} + + +static void SetExtraIcons(char *name, int *id) +{ + char *p = strchr(name, '.'); + + if (p != NULL) + *p = '\0'; + + ExtraFolderIcons[numExtraIcons] = (char*)malloc(strlen(name) + 1); + if (ExtraFolderIcons[numExtraIcons]) + { + *id = ICON_MAX + numExtraIcons; + strcpy(ExtraFolderIcons[numExtraIcons], name); + numExtraIcons++; + } +} + + +// Called to add child folders of the top level extra folders already created +BOOL TryAddExtraFolderAndChildren(int parent_index) +{ + FILE* fp = NULL; + char fname[MAX_PATH]; + char readbuf[256]; + char* p; + char* name; + int id, current_id, i; + LPTREEFOLDER lpTemp = NULL; + LPTREEFOLDER lpFolder = treeFolders[parent_index]; + + current_id = lpFolder->m_nFolderId; + + id = lpFolder->m_nFolderId - MAX_FOLDERS; + + /* "folder\title.ini" */ + + const std::string t = GetFolderDir(); + sprintf( fname, "%s\\%s.ini", t.c_str(), ExtraFolderData[id]->m_szTitle); + + fp = fopen(fname, "r"); + if (fp == NULL) + return FALSE; + + while ( fgets(readbuf, 256, fp) ) + { + /* do we have [...] ? */ + + if (readbuf[0] == '[') + { + p = strchr(readbuf, ']'); + if (p == NULL) + { + continue; + } + + *p = '\0'; + name = &readbuf[1]; + + /* is it [FOLDER_SETTINGS]? */ + + if (strcmp(name, "FOLDER_SETTINGS") == 0) + { + current_id = -1; + continue; + } + else + { + /* it it [ROOT_FOLDER]? */ + + if (!strcmp(name, "ROOT_FOLDER")) + { + current_id = lpFolder->m_nFolderId; + lpTemp = lpFolder; + + } + else + { + /* must be [folder name] */ + + current_id = next_folder_id++; + /* create a new folder with this name, + and the flags for this folder as read from the registry */ + lpTemp = NewFolder(name,current_id,parent_index, + ExtraFolderData[id]->m_nSubIconId, + GetFolderFlags(numFolders) | F_CUSTOM); + ExtraFolderData[current_id] = (EXFOLDERDATA*)malloc(sizeof(EXFOLDERDATA)); + memset(ExtraFolderData[current_id], 0, sizeof(EXFOLDERDATA)); + + ExtraFolderData[current_id]->m_nFolderId = current_id - MAX_EXTRA_FOLDERS; + ExtraFolderData[current_id]->m_nIconId = ExtraFolderData[id]->m_nSubIconId; + ExtraFolderData[current_id]->m_nParent = ExtraFolderData[id]->m_nFolderId; + ExtraFolderData[current_id]->m_nSubIconId = -1; + strcpy( ExtraFolderData[current_id]->m_szTitle, name ); + ExtraFolderData[current_id]->m_dwFlags = ExtraFolderData[id]->m_dwFlags; + AddFolder(lpTemp); + } + } + } + else if (current_id != -1) + { + /* string on a line by itself -- game name */ + + name = strtok(readbuf, " \t\r\n"); + if (name == NULL) + { + current_id = -1; + continue; + } + + /* IMPORTANT: This assumes that all driver names are lowercase! */ + for (i = 0; name[i]; i++) + name[i] = tolower(name[i]); + + if (lpTemp == NULL) + { + ErrorMsg("Error parsing %s: missing [folder name] or [ROOT_FOLDER]", fname); + current_id = lpFolder->m_nFolderId; + lpTemp = lpFolder; + } + AddGame(lpTemp,GetGameNameIndex(name)); + } + } + + if ( fp ) + { + fclose( fp ); + } + + return TRUE; +} + + +void GetFolders(TREEFOLDER ***folders,int *num_folders) +{ + *folders = treeFolders; + *num_folders = numFolders; +} + +static BOOL TryRenameCustomFolderIni(LPTREEFOLDER lpFolder,const char *old_name,const char *new_name) +{ + char filename[MAX_PATH]; + char new_filename[MAX_PATH]; + LPTREEFOLDER lpParent = NULL; + if (lpFolder->m_nParent >= 0) + { + //it is a custom SubFolder + lpParent = GetFolder( lpFolder->m_nParent ); + if( lpParent ) + { + snprintf(filename,ARRAY_LENGTH(filename),"%s\\%s\\%s.ini",GetIniDir(),lpParent->m_lpTitle, old_name ); + snprintf(new_filename,ARRAY_LENGTH(new_filename),"%s\\%s\\%s.ini",GetIniDir(),lpParent->m_lpTitle, new_name ); + win_move_file_utf8(filename,new_filename); + } + } + else + { + //Rename the File, if it exists + snprintf(filename,ARRAY_LENGTH(filename),"%s\\%s.ini",GetIniDir(),old_name ); + snprintf(new_filename,ARRAY_LENGTH(new_filename),"%s\\%s.ini",GetIniDir(), new_name ); + win_move_file_utf8(filename,new_filename); + //Rename the Directory, if it exists + snprintf(filename,ARRAY_LENGTH(filename),"%s\\%s",GetIniDir(),old_name ); + snprintf(new_filename,ARRAY_LENGTH(new_filename),"%s\\%s",GetIniDir(), new_name ); + win_move_file_utf8(filename,new_filename); + } + return TRUE; +} + +BOOL TryRenameCustomFolder(LPTREEFOLDER lpFolder, const char *new_name) +{ + BOOL retval; + char filename[MAX_PATH]; + char new_filename[MAX_PATH]; + + if (lpFolder->m_nParent >= 0) + { + // a child extra folder was renamed, so do the rename and save the parent + + // save old title + char *old_title = lpFolder->m_lpTitle; + + // set new title + lpFolder->m_lpTitle = (char *)malloc(strlen(new_name) + 1); + strcpy(lpFolder->m_lpTitle,new_name); + + // try to save + if (TrySaveExtraFolder(lpFolder) == FALSE) + { + // failed, so free newly allocated title and restore old + free(lpFolder->m_lpTitle); + lpFolder->m_lpTitle = old_title; + return FALSE; + } + TryRenameCustomFolderIni(lpFolder, old_title, new_name); + // successful, so free old title + free(old_title); + return TRUE; + } + + // a parent extra folder was renamed, so rename the file + + const std::string t = GetFolderDir(); + snprintf(new_filename,ARRAY_LENGTH(new_filename),"%s\\%s.ini", t.c_str(), new_name); + snprintf(filename,ARRAY_LENGTH(filename),"%s\\%s.ini", t.c_str(), lpFolder->m_lpTitle); + + retval = win_move_file_utf8(filename,new_filename); + + if (retval) + { + TryRenameCustomFolderIni(lpFolder, lpFolder->m_lpTitle, new_name); + free(lpFolder->m_lpTitle); + lpFolder->m_lpTitle = (char *)malloc(strlen(new_name) + 1); + strcpy(lpFolder->m_lpTitle,new_name); + } + else + { + char buf[500]; + snprintf(buf,ARRAY_LENGTH(buf),"Error while renaming custom file %s to %s", filename,new_filename); + win_message_box_utf8(GetMainWindow(), buf, MAMEUINAME, MB_OK | MB_ICONERROR); + } + return retval; +} + +void AddToCustomFolder(LPTREEFOLDER lpFolder,int driver_index) +{ + if ((lpFolder->m_dwFlags & F_CUSTOM) == 0) + { + win_message_box_utf8(GetMainWindow(),"Unable to add game to non-custom folder", + MAMEUINAME,MB_OK | MB_ICONERROR); + return; + } + + if (TestBit(lpFolder->m_lpGameBits,driver_index) == 0) + { + AddGame(lpFolder,driver_index); + if (TrySaveExtraFolder(lpFolder) == FALSE) + RemoveGame(lpFolder,driver_index); // undo on error + } +} + +void RemoveFromCustomFolder(LPTREEFOLDER lpFolder,int driver_index) +{ + if ((lpFolder->m_dwFlags & F_CUSTOM) == 0) + { + win_message_box_utf8(GetMainWindow(),"Unable to remove game from non-custom folder", + MAMEUINAME,MB_OK | MB_ICONERROR); + return; + } + + if (TestBit(lpFolder->m_lpGameBits,driver_index) != 0) + { + RemoveGame(lpFolder,driver_index); + if (TrySaveExtraFolder(lpFolder) == FALSE) + AddGame(lpFolder,driver_index); // undo on error + } +} + +BOOL TrySaveExtraFolder(LPTREEFOLDER lpFolder) +{ + char fname[MAX_PATH]; + FILE *fp; + BOOL error = FALSE; + int i,j; + + LPTREEFOLDER root_folder = NULL; + LPEXFOLDERDATA extra_folder = NULL; + + for (i=0;i<numExtraFolders;i++) + { + if (ExtraFolderData[i]->m_nFolderId == lpFolder->m_nFolderId) + { + root_folder = lpFolder; + extra_folder = ExtraFolderData[i]; + break; + } + + if (lpFolder->m_nParent >= 0 && + ExtraFolderData[i]->m_nFolderId == treeFolders[lpFolder->m_nParent]->m_nFolderId) + { + root_folder = treeFolders[lpFolder->m_nParent]; + extra_folder = ExtraFolderData[i]; + break; + } + } + + if (extra_folder == NULL || root_folder == NULL) + { + MessageBox(GetMainWindow(), TEXT("Error finding custom file name to save"), (LPCTSTR)MAMEUINAME, MB_OK | MB_ICONERROR); + return FALSE; + } + /* "folder\title.ini" */ + + const std::string t = GetFolderDir(); + snprintf( fname, sizeof(fname), "%s\\%s.ini", t.c_str(), extra_folder->m_szTitle); + + fp = fopen(fname, "wt"); + if (fp == NULL) + error = TRUE; + else + { + TREEFOLDER *folder_data; + + + fprintf(fp,"[FOLDER_SETTINGS]\n"); + // negative values for icons means it's custom, so save 'em + if (extra_folder->m_nIconId < 0) + { + fprintf(fp, "RootFolderIcon %s\n", ExtraFolderIcons[(-extra_folder->m_nIconId) - ICON_MAX]); + } + if (extra_folder->m_nSubIconId < 0) + { + fprintf(fp,"SubFolderIcon %s\n", ExtraFolderIcons[(-extra_folder->m_nSubIconId) - ICON_MAX]); + } + + /* need to loop over all our TREEFOLDERs--first the root one, then each child. + start with the root */ + + folder_data = root_folder; + + fprintf(fp,"\n[ROOT_FOLDER]\n"); + + for (i=0;i<driver_list::total();i++) + { + if (TestBit(folder_data->m_lpGameBits, i)) + { + fprintf(fp,"%s\n",driver_list::driver(i).name); + } + } + + /* look through the custom folders for ones with our root as parent */ + for (j=0;j<numFolders;j++) + { + folder_data = treeFolders[j]; + + if (folder_data->m_nParent >= 0 && + treeFolders[folder_data->m_nParent] == root_folder) + { + fprintf(fp,"\n[%s]\n",folder_data->m_lpTitle); + + for (i=0;i<driver_list::total();i++) + { + if (TestBit(folder_data->m_lpGameBits, i)) + { + fprintf(fp,"%s\n",driver_list::driver(i).name); + } + } + } + } + if (fclose(fp) != 0) + error = TRUE; + } + + if (error) + { + char buf[500]; + snprintf(buf,ARRAY_LENGTH(buf),"Error while saving custom file %s",fname); + win_message_box_utf8(GetMainWindow(), buf, MAMEUINAME, MB_OK | MB_ICONERROR); + } + return !error; +} + +HIMAGELIST GetTreeViewIconList(void) +{ + return hTreeSmall; +} + +int GetTreeViewIconIndex(int icon_id) +{ + int i; + + if (icon_id < 0) + return -icon_id; + + for (i = 0; i < ARRAY_LENGTH(treeIconNames); i++) + { + if (icon_id == treeIconNames[i].nResourceID) + return i; + } + + return -1; +} + +static void SaveExternalFolders(int parent_index, const char *fname) +{ + string val = GetFolderDir(); + char s[val.size()+1]; + strcpy(s, val.c_str()); + char *fdir = strtok(s, ";"); // get first dir + + // create directory if needed + wchar_t *temp = ui_wstring_from_utf8(fdir); + BOOL res = CreateDirectory(temp, NULL); + free(temp); + if (!res) + { + if (GetLastError() == ERROR_PATH_NOT_FOUND) + { + printf("SaveExternalFolders: Unable to create the directory \"%s\".\n",fdir); + return; + } + } + + // create/truncate file + string filename = fdir + string("\\") + fname + string(".ini"); + FILE *f = fopen(filename.c_str(), "w"); + if (f == NULL) + { + printf("SaveExternalFolders: Unable to open file %s for writing.\n",filename.c_str()); + return; + } + + // Populate the file + fprintf(f, "[FOLDER_SETTINGS]\n"); + fprintf(f, "RootFolderIcon custom\n"); + fprintf(f, "SubFolderIcon custom\n"); + + /* need to loop over all our TREEFOLDERs--first the root one, then each child. + start with the root */ + LPTREEFOLDER lpFolder = treeFolders[parent_index]; + TREEFOLDER *folder_data = lpFolder; + fprintf(f, "\n[ROOT_FOLDER]\n"); + + int i; + for (i = 0; i < driver_list::total(); i++) + { + if (TestBit(folder_data->m_lpGameBits, i)) + fprintf(f, "%s\n", GetGameName(i).c_str()); + } + + /* look through the custom folders for ones with our root as parent */ + for (int jj = 0; jj < numFolders; jj++) + { + folder_data = treeFolders[jj]; + + if (folder_data->m_nParent >= 0 && treeFolders[folder_data->m_nParent] == lpFolder) + { + fprintf(f, "\n[%s]\n", folder_data->m_lpTitle); + + for (i = 0; i < driver_list::total(); i++) + { + if (TestBit(folder_data->m_lpGameBits, i)) + fprintf(f, "%s\n", GetGameName(i).c_str()); + } + } + } + + fclose(f); + printf("SaveExternalFolders: Saved file %s.\n",filename.c_str()); +} + +/* End of source file */ |