Files
Project64-Legacy/RomBrowser.cpp
Icepir8 cf18e46ac7 #bugfix
synched rom browser and main emulation windows.
fixed window drift when taskbar is on the top or left.
2023-08-19 16:31:49 +02:00

1458 lines
41 KiB
C++

/*
* Project 64 - A Nintendo 64 emulator.
*
* (c) Copyright 2001 zilmar (zilmar@emulation64.com) and
* Jabo (jabo@emulation64.com).
*
* pj64 homepage: www.pj64.net
*
* Permission to use, copy, modify and distribute Project64 in both binary and
* source form, for non-commercial purposes, is hereby granted without fee,
* providing that this license information and copyright notice appear with
* all copies and any derived work.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event shall the authors be held liable for any damages
* arising from the use of this software.
*
* Project64 is freeware for PERSONAL USE only. Commercial users should
* seek permission of the copyright holders first. Commercial use includes
* charging money for Project64 or software derived from Project64.
*
* The copyright holders request that bug fixes and improvements to the code
* should be forwarded to them so if they want them.
*
*/
//#define SHOW_SCAN_TIME
#include <Windows.h>
#include <commctrl.h>
#include <shlwapi.h>
#include <shlobj.h>
#include <stdio.h>
#include <time.h>
#include <tchar.h>
#include "main.h"
#include "cpu.h"
#include "plugin.h"
#include "resource.h"
#include "RomTools_Common.h"
#include <time.h>
#include "pif.h"
#include <vector>
#include <string>
#include <algorithm>
#define NoOfSortKeys 3
#define RB_FileName 0
#define RB_InternalName 1
#define RB_GoodName 2
#define RB_Status 3
#define RB_RomSize 4
#define RB_CoreNotes 5
#define RB_PluginNotes 6
#define RB_UserNotes 7
#define RB_CartridgeID 8
#define RB_ReleaseVer 9
#define RB_SdkVer 10
#define RB_Manufacturer 11
#define RB_Country 12
#define RB_Developer 13
#define RB_Crc1 14
#define RB_Crc2 15
#define RB_CICChip 16
#define RB_ReleaseDate 17
#define RB_Genre 18
#define RB_Players 19
#define RB_ForceFeedback 20
#define COLOR_TEXT 0
#define COLOR_SELECTED_TEXT 1
#define COLOR_HIGHLIGHTED 2
typedef struct {
char szFullFileName[MAX_PATH + 1];
char Status[60];
char FileName[200];
char InternalName[22];
char GoodName[200];
char CartID[3];
char PluginNotes[250];
char CoreNotes[250];
char UserNotes[250];
char Developer[30];
char ReleaseDate[30];
char Genre[15];
int Players;
int RomSize;
int ReleaseVersion;
BYTE SdkVersion[2];
BYTE Manufacturer;
BYTE Country;
DWORD CRC1;
DWORD CRC2;
enum CIC_CHIP CicChip;
char ForceFeedback[15];
} ROM_INFO;
typedef struct {
int Key[NoOfSortKeys];
BOOL KeyAscend[NoOfSortKeys];
} SORT_FIELDS;
typedef struct {
char* status_name;
COLORREF HighLight;
COLORREF Text;
COLORREF SelectedText;
} COLOR_ENTRY;
// Function Prototypes
void GetSortField(int Index, char* ret, int max);
void LoadRomList();
void RomList_SortList();
void RomList_SelectFind(char* match);
void FillRomExtensionInfo(ROM_INFO* pRomInfo);
BOOL FillRomInfo(ROM_INFO* pRomInfo);
void SetSortAscending(BOOL Ascending, int Index);
void SetSortField(char* FieldName, int Index);
void SaveRomList();
COLORREF GetColor(char* status, int selection);
int ColorIndex(char* status);
void SetColors(char* status);
void FillRomList(char* Directory);
DWORD WINAPI RefreshRomBrowserMT(LPVOID lpArgs);
DWORD WINAPI UpdateBrowser(LPVOID lpArgs);
// Global accessed by C code, do not change
char CurrentRBFileName[MAX_PATH + 1] = { "" };
// Global accessed by C code, do not change
ROMBROWSER_FIELDS RomBrowserFields[] =
{
"File Name", -1, RB_FileName, 218, RB_FILENAME,
"Internal Name", -1, RB_InternalName, 200, RB_INTERNALNAME,
"Good Name", 0, RB_GoodName, 218, RB_GOODNAME,
"Status", 1, RB_Status, 92, RB_STATUS,
"Rom Size", -1, RB_RomSize, 100, RB_ROMSIZE,
"Notes (Core)", 2, RB_CoreNotes, 120, RB_NOTES_CORE,
"Notes (default plugins)", 3, RB_PluginNotes, 188, RB_NOTES_PLUGIN,
"Notes (User)", -1, RB_UserNotes, 100, RB_NOTES_USER,
"Cartridge ID", -1, RB_CartridgeID, 100, RB_CART_ID,
"Release Version", -1, RB_ReleaseVer, 100, RB_RELEASE_VER,
"SDK Version", -1, RB_SdkVer, 100, RB_SDK_VER,
"Manufacturer", -1, RB_Manufacturer, 100, RB_MANUFACTUER,
"Country", -1, RB_Country, 100, RB_COUNTRY,
"Developer", -1, RB_Developer, 100, RB_DEVELOPER,
"CRC1", -1, RB_Crc1, 100, RB_CRC1,
"CRC2", -1, RB_Crc2, 100, RB_CRC2,
"CIC Chip", -1, RB_CICChip, 100, RB_CICCHIP,
"Release Date", -1, RB_ReleaseDate, 100, RB_RELEASE_DATE,
"Genre", -1, RB_Genre, 100, RB_GENRE,
"Players", -1, RB_Players, 100, RB_PLAYERS,
"Force Feedback", 4, RB_ForceFeedback, 100, RB_FORCE_FEEDBACK,
};
// Global accessed by C code, do not change
int NoOfFields = sizeof(RomBrowserFields) / sizeof(RomBrowserFields[0]);
HWND hRomList = NULL;
int FieldType[(sizeof(RomBrowserFields) / sizeof(RomBrowserFields[0])) + 1];
// Threading related globals
BOOL scanning = FALSE, cancelled = TRUE, pendingUpdate = FALSE;
HANDLE gRBMutex = NULL, romThread = NULL;
// Most data relevant to the rom browser is stored here, the rom list, the color list, and related sorting fields
std::vector<ROM_INFO> RomList;
std::vector<COLOR_ENTRY> ColorList;
SORT_FIELDS gSortFields = { 0 };
// Threaded test
DWORD WINAPI GetRomInfo(LPVOID lpArgs) {
ROM_INFO* transfer = (ROM_INFO*)lpArgs;
// Failure to fill rom info, use szFullFileName to denote an error by clearing it
if (!FillRomInfo(transfer))
strcpy(transfer->szFullFileName, "");
return 0;
}
void AddRomToList(char* RomLocation) {
DWORD wait_result;
ROM_INFO temp = { 0 };
strncpy(temp.szFullFileName, RomLocation, MAX_PATH);
if (!FillRomInfo(&temp))
return;
wait_result = WaitForSingleObject(gRBMutex, INFINITE);
// Mutex ownership acquired, okay to continue
if (wait_result == WAIT_OBJECT_0) {
RomList.push_back(temp);
}
// No longer require mutual exclusion
if (!ReleaseMutex(gRBMutex))
MessageBox(NULL, "Failed to release a mutex???", "Error", MB_OK);
// Corruption? Mutex was released by operating system, just bail and reuse memory allocation error
if (wait_result == WAIT_ABANDONED) {
DisplayError(GS(MSG_MEM_ALLOC_ERROR));
ExitThread(0);
}
}
void CreateRomListControl(HWND hParent) {
DWORD dwStyle;
hRomList = CreateWindow(WC_LISTVIEW, NULL,
WS_TABSTOP | WS_VISIBLE | WS_CHILD |
LVS_OWNERDATA | LVS_OWNERDRAWFIXED | LVS_SINGLESEL | LVS_REPORT,
0, 0, 0, 0, hParent, (HMENU)IDC_ROMLIST, hInst, NULL);
// Double buffering! Useful to keep the flicker down
dwStyle = ListView_GetExtendedListViewStyle(hRomList) | LVS_EX_DOUBLEBUFFER;
ListView_SetExtendedListViewStyle(hRomList, dwStyle);
// Will be used to control when the listview can be updated
gRBMutex = CreateMutex(NULL, FALSE, NULL);
ResetRomBrowserColomuns();
LoadRomList();
}
void FixRomBrowserColumnLang(void) {
ResetRomBrowserColomuns();
}
void HideRomBrowser(void) {
DWORD X, Y;
long Style;
if (CPURunning) { return; }
if (hRomList == NULL) { return; }
IgnoreMove = TRUE;
if (IsRomBrowserMaximized())
ShowWindow(hMainWindow, SW_RESTORE);
ShowWindow(hMainWindow, SW_HIDE);
Style = GetWindowLong(hMainWindow, GWL_STYLE) & ~(WS_SIZEBOX | WS_MAXIMIZEBOX);
SetWindowLong(hMainWindow, GWL_STYLE, Style);
if (GetStoredWinPos("Main", &X, &Y))
SetWindowPos(hMainWindow, NULL, X, Y, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
EnableWindow(hRomList, FALSE);
ShowWindow(hRomList, SW_HIDE);
SetupPlugins(hMainWindow);
SendMessage(hMainWindow, WM_USER + 17, 0, 0);
ShowWindow(hMainWindow, SW_SHOW);
IgnoreMove = FALSE;
}
BOOL IsRomBrowserMaximized(void) {
return Settings_ReadBool(APPS_NAME, "Rom Browser Page", "Maximized", FALSE);
}
BOOL IsSortAscending(int Index) {
char Search[200];
sprintf(Search, "Sort Ascending %d", Index);
return Settings_ReadBool(APPS_NAME, "Rom Browser Page", Search, FALSE);
}
void LoadRomList(void) {
char path_buffer[_MAX_PATH], drive[_MAX_DRIVE], dir[_MAX_DIR];
char fname[_MAX_FNAME], ext[_MAX_EXT];
char FileName[_MAX_PATH];
int Size, count;
DWORD dwRead;
HANDLE hFile;
ROM_INFO temp;
// Clear the rom browser
FreeRomBrowser();
ListView_SetItemCount(hRomList, 0);
GetModuleFileName(NULL, path_buffer, sizeof(path_buffer));
_splitpath(path_buffer, drive, dir, fname, ext);
sprintf(FileName, "%s%s%s", drive, dir, ROC_NAME);
hFile = CreateFile(FileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
RefreshRomBrowser();
return;
}
// The first 4 bytes contains the size of ROM_INFO
Size = 0;
ReadFile(hFile, &Size, sizeof(Size), &dwRead, NULL);
if (Size != sizeof(ROM_INFO) || dwRead != sizeof(Size)) {
CloseHandle(hFile);
RefreshRomBrowser();
return;
}
// The next 4 bytes contains the amount of entries
ReadFile(hFile, &count, sizeof(count), &dwRead, NULL);
if (count == 0) {
CloseHandle(hFile);
RefreshRomBrowser();
return;
}
RomList.reserve(count);
// Read each entry and add it to the vector
while (count != 0) {
ReadFile(hFile, &temp, sizeof(temp), &dwRead, NULL);
RomList.push_back(temp);
// Make sure the color cache for the status is set
SetColors(RomList.back().Status);
// Read extra information from the files
FillRomExtensionInfo(&RomList.back());
--count;
}
CloseHandle(hFile);
RomList_SortList();
ListView_SetItemCount(hRomList, RomList.size());
RomList_SelectFind(LastRoms[0]);
}
void LoadRomBrowserColumnInfo(void) {
char String[200];
int count;
for (count = 0; count < NoOfFields; count++) {
// Column Position
RomBrowserFields[count].Pos = Settings_ReadInt(APPS_NAME, "Rom Browser", RomBrowserFields[count].Name, RomBrowserFields[count].Pos);
// Column Width
sprintf(String, "%s.Width", RomBrowserFields[count].Name);
RomBrowserFields[count].ColWidth = Settings_ReadInt(APPS_NAME, "Rom Browser", String, RomBrowserFields[count].ColWidth);
}
FixRomBrowserColumnLang();
}
void FillRomExtensionInfo(ROM_INFO* pRomInfo) {
char Identifier[100], * read;
RomIDPreScanned(Identifier, &pRomInfo->CRC1, &pRomInfo->CRC2, &pRomInfo->Country);
//Rom Notes
if (RomBrowserFields[RB_UserNotes].Pos >= 0) {
Settings_Read(RDN_NAME, Identifier, "Note", "", &read);
strncpy(pRomInfo->UserNotes, read, sizeof(pRomInfo->UserNotes));
if (read) free(read);
}
//Rom Extension info
if (RomBrowserFields[RB_Developer].Pos >= 0) {
Settings_Read(RDI_NAME, Identifier, "Developer", "", &read);
strncpy(pRomInfo->Developer, read, sizeof(pRomInfo->Developer));
if (read) free(read);
}
if (RomBrowserFields[RB_ReleaseDate].Pos >= 0) {
Settings_Read(RDI_NAME, Identifier, "ReleaseDate", "", &read);
strncpy(pRomInfo->ReleaseDate, read, sizeof(pRomInfo->ReleaseDate));
if (read) free(read);
}
if (RomBrowserFields[RB_Genre].Pos >= 0) {
Settings_Read(RDI_NAME, Identifier, "Genre", "", &read);
strncpy(pRomInfo->Genre, read, sizeof(pRomInfo->Genre));
if (read) free(read);
}
if (RomBrowserFields[RB_Players].Pos >= 0)
pRomInfo->Players = Settings_ReadInt(RDI_NAME, Identifier, "Players", 1);
if (RomBrowserFields[RB_ForceFeedback].Pos >= 0) {
Settings_Read(RDI_NAME, Identifier, "ForceFeedback", "", &read);
strncpy(pRomInfo->ForceFeedback, read, sizeof(pRomInfo->ForceFeedback));
if (read) free(read);
}
//Rom Settings
if (RomBrowserFields[RB_GoodName].Pos >= 0) {
Settings_Read(RDS_NAME, Identifier, "Name", GS(RB_NOT_GOOD_FILE), &read);
strncpy(pRomInfo->GoodName, read, sizeof(pRomInfo->GoodName));
if (read) free(read);
}
if (RomBrowserFields[RB_Status].Pos >= 0) {
Settings_Read(RDS_NAME, Identifier, "Status", Default_RomStatus, &read);
strncpy(pRomInfo->Status, read, sizeof(pRomInfo->Status));
if (read) free(read);
}
if (RomBrowserFields[RB_CoreNotes].Pos >= 0) {
Settings_Read(RDS_NAME, Identifier, "Core Note", "", &read);
strncpy(pRomInfo->CoreNotes, read, sizeof(pRomInfo->CoreNotes));
if (read) free(read);
}
if (RomBrowserFields[RB_PluginNotes].Pos >= 0) {
Settings_Read(RDS_NAME, Identifier, "Plugin Note", "", &read);
strncpy(pRomInfo->PluginNotes, read, sizeof(pRomInfo->PluginNotes));
if (read) free(read);
}
SetColors(pRomInfo->Status);
}
BOOL FillRomInfo(ROM_INFO* pRomInfo) {
BYTE RomData[0x1000];
// This is the experimental work for using xxhash, comment it out for now
//if (!LoadDataForRomBrowser(pRomInfo->szFullFileName, RomData, sizeof(RomData), &pRomInfo->RomSize))
// return FALSE;
if (!LoadDataFromRomFile(pRomInfo->szFullFileName, RomData, sizeof(RomData), &pRomInfo->RomSize))
return FALSE;
_splitpath(pRomInfo->szFullFileName, NULL, NULL, pRomInfo->FileName, NULL);
GetRomName(pRomInfo->InternalName, RomData);
GetRomCartID(pRomInfo->CartID, RomData);
GetRomReleaseVersion(&pRomInfo->ReleaseVersion, RomData);
GetRomSdkVersion(pRomInfo->SdkVersion, RomData);
GetRomManufacturer(&pRomInfo->Manufacturer, RomData);
GetRomCountry(&pRomInfo->Country, RomData);
GetRomCRC1(&pRomInfo->CRC1, RomData);
GetRomCRC2(&pRomInfo->CRC2, RomData);
pRomInfo->CicChip = GetRomCicChipID(RomData);
FillRomExtensionInfo(pRomInfo);
return TRUE;
}
int GetRomBrowserSize(DWORD* nWidth, DWORD* nHeight) {
*nWidth = Settings_ReadInt(APPS_NAME, "Rom Browser Page", "Width", -1);
*nHeight = Settings_ReadInt(APPS_NAME, "Rom Browser Page", "Height", -1);
if (*nWidth == -1 || *nHeight == -1)
return FALSE;
return TRUE;
}
void GetSortField(int Index, char* ret, int max) {
char String[200], * read;
sprintf(String, "Sort Field %d", Index);
Settings_Read(APPS_NAME, "Rom Browser Page", String, "", &read);
strncpy(ret, read, max);
if (read) free(read);
}
void RefreshRomBrowser(void) {
// Update to not scan if the rom list is hidden
if (hRomList && !IsWindowEnabled(hRomList)) {
FreeRomBrowser();
SaveRomList();
pendingUpdate = TRUE;
return;
}
if (!hRomList)
return;
if (scanning) {
MessageBox(NULL, "Still Scanning!", "Wait up!", MB_OK);
return;
}
ListView_SetItemCount(hRomList, 0);
FreeRomBrowser();
romThread = CreateThread(NULL, 0, RefreshRomBrowserMT, NULL, 0, NULL);
}
void ResetRomBrowserColomuns(void) {
int Column, index;
LV_COLUMN lvColumn;
char szString[300];
//SaveRomBrowserColumnInfo();
memset(&lvColumn, 0, sizeof(lvColumn));
lvColumn.mask = LVCF_FMT;
while (ListView_GetColumn(hRomList, 0, &lvColumn)) {
ListView_DeleteColumn(hRomList, 0);
}
//Add Colomuns
lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.pszText = szString;
for (Column = 0; Column < NoOfFields; Column++) {
for (index = 0; index < NoOfFields; index++) {
if (RomBrowserFields[index].Pos == Column) { break; }
}
if (index == NoOfFields || RomBrowserFields[index].Pos != Column) {
FieldType[Column] = -1;
break;
}
FieldType[Column] = RomBrowserFields[index].ID;
lvColumn.cx = RomBrowserFields[index].ColWidth;
strncpy(szString, GS(RomBrowserFields[index].LangID), sizeof(szString));
ListView_InsertColumn(hRomList, Column, &lvColumn);
}
}
void ResizeRomListControl(WORD nWidth, WORD nHeight) {
if (IsWindow(hRomList)) {
if (IsWindow(hStatusWnd)) {
RECT rc;
GetWindowRect(hStatusWnd, &rc);
nHeight -= (WORD)(rc.bottom - rc.top);
}
MoveWindow(hRomList, 0, 0, nWidth, nHeight, TRUE);
}
}
void RomList_ColumnSortList(LPNMLISTVIEW pnmv) {
int index, iItem;
char selected_filename[261], String[200];
// Save the previously selected item
iItem = ListView_GetNextItem(hRomList, -1, LVNI_SELECTED);
if (iItem != -1)
strcpy(selected_filename, RomList.at(iItem).szFullFileName);
for (index = 0; index < NoOfFields; index++) {
if (RomBrowserFields[index].Pos == pnmv->iSubItem) { break; }
}
if (NoOfFields == index) { return; }
GetSortField(0, String, sizeof(String));
if (_stricmp(String, RomBrowserFields[index].Name) == 0) {
SetSortAscending(!IsSortAscending(0), 0);
}
else {
int count;
for (count = NoOfSortKeys - 1; count > 0; count--) {
GetSortField(count - 1, String, sizeof(String));
if (strlen(String) > 0) {
SetSortField(String, count);
SetSortAscending(IsSortAscending(count - 1), count);
}
}
SetSortField(RomBrowserFields[index].Name, 0);
SetSortAscending(TRUE, 0);
}
RomList_SortList();
ListView_RedrawItems(hRomList, 0, RomList.size());
// Make sure the last item selected is still selected
if (iItem != -1) {
RomList_SelectFind(selected_filename);
}
}
bool RomList_Compare(const ROM_INFO& a, const ROM_INFO& b) {
ROM_INFO pRomInfo1, pRomInfo2;
int count, compare;
for (count = 0; count < NoOfSortKeys; count++) {
pRomInfo1 = gSortFields.KeyAscend[count] ? a : b;
pRomInfo2 = gSortFields.KeyAscend[count] ? b : a;
switch (gSortFields.Key[count]) {
case RB_FileName:
compare = lstrcmpi(pRomInfo1.FileName, pRomInfo2.FileName);
break;
case RB_InternalName:
compare = lstrcmpi(pRomInfo1.InternalName, pRomInfo2.InternalName);
break;
case RB_GoodName:
compare = lstrcmpi(pRomInfo1.GoodName, pRomInfo2.GoodName);
break;
case RB_Status:
compare = lstrcmpi(pRomInfo1.Status, pRomInfo2.Status);
break;
case RB_RomSize:
compare = pRomInfo1.RomSize - pRomInfo2.RomSize;
break;
case RB_CoreNotes:
compare = lstrcmpi(pRomInfo1.CoreNotes, pRomInfo2.CoreNotes);
break;
case RB_PluginNotes:
compare = lstrcmpi(pRomInfo1.PluginNotes, pRomInfo2.PluginNotes);
break;
case RB_UserNotes:
compare = lstrcmpi(pRomInfo1.UserNotes, pRomInfo2.UserNotes);
break;
case RB_CartridgeID:
compare = lstrcmpi(pRomInfo1.CartID, pRomInfo2.CartID);
break;
case RB_ReleaseVer:
compare = (int)pRomInfo1.ReleaseVersion - (int)pRomInfo2.ReleaseVersion;
break;
case RB_SdkVer:
compare = (int)(*(SHORT*)pRomInfo1.SdkVersion) - (int)(*(SHORT*)pRomInfo2.SdkVersion);
break;
case RB_Manufacturer:
compare = (int)pRomInfo1.Manufacturer - (int)pRomInfo2.Manufacturer;
break;
case RB_Country:
{
char junk1[50], junk2[50];
CountryCodeToString(junk1, pRomInfo1.Country, sizeof(junk1));
CountryCodeToString(junk2, pRomInfo2.Country, sizeof(junk2));
compare = lstrcmpi(junk1, junk2);
break;
}
case RB_Developer:
compare = lstrcmpi(pRomInfo1.Developer, pRomInfo2.Developer);
break;
case RB_Crc1:
{
char crc_str1[9], crc_str2[9];
sprintf(crc_str1, "%08x", pRomInfo1.CRC1);
sprintf(crc_str2, "%08x", pRomInfo2.CRC1);
compare = lstrcmpi(crc_str1, crc_str2);
break;
}
case RB_Crc2:
{
char crc_str1[9], crc_str2[9];
sprintf(crc_str1, "%08x", pRomInfo1.CRC2);
sprintf(crc_str2, "%08x", pRomInfo2.CRC2);
compare = lstrcmpi(crc_str1, crc_str2);
break;
}
case RB_CICChip:
{
char junk1[50], junk2[50];
BuildRomCicChipString(pRomInfo1.CicChip, junk1, sizeof(junk1), GetRomRegionByCode(pRomInfo1.Country));
BuildRomCicChipString(pRomInfo2.CicChip, junk2, sizeof(junk2), GetRomRegionByCode(pRomInfo2.Country));
compare = lstrcmpi(junk1, junk2);
break;
}
case RB_ReleaseDate:
compare = lstrcmpi(pRomInfo1.ReleaseDate, pRomInfo2.ReleaseDate);
break;
case RB_Players:
compare = pRomInfo1.Players - pRomInfo2.Players;
break;
case RB_ForceFeedback:
compare = lstrcmpi(pRomInfo1.ForceFeedback, pRomInfo2.ForceFeedback);
break;
case RB_Genre:
compare = lstrcmpi(pRomInfo1.Genre, pRomInfo2.Genre);
break;
default:
compare = 0;
break;
}
if (compare > 0) {
// a > b (compare is returning 1, so a is greater than b)
return false;
} else if (compare == 0) {
// Same, continue the compare by using the other sort keys
break;
} else {
// a < b (compare is returning -1, so a is less than b)
return true;
}
}
return false;
}
void RomList_GetDispInfo(LPNMHDR pnmh) {
LV_DISPINFO* lpdi = (LV_DISPINFO*)pnmh;
ROM_INFO* pRomInfo = &RomList.at(lpdi->item.iItem);
// Do not continue if the request does not contain a valid text field (Windows XP was having issues here)
if (!(lpdi->item.mask & LVIF_TEXT))
return;
switch (FieldType[lpdi->item.iSubItem]) {
case RB_FileName:
strncpy(lpdi->item.pszText, pRomInfo->FileName, lpdi->item.cchTextMax);
break;
case RB_InternalName:
strncpy(lpdi->item.pszText, pRomInfo->InternalName, lpdi->item.cchTextMax);
break;
case RB_GoodName:
strncpy(lpdi->item.pszText, pRomInfo->GoodName, lpdi->item.cchTextMax);
break;
case RB_CoreNotes:
strncpy(lpdi->item.pszText, pRomInfo->CoreNotes, lpdi->item.cchTextMax);
break;
case RB_PluginNotes:
strncpy(lpdi->item.pszText, pRomInfo->PluginNotes, lpdi->item.cchTextMax);
break;
case RB_Status:
strncpy(lpdi->item.pszText, pRomInfo->Status, lpdi->item.cchTextMax);
break;
case RB_RomSize:
sprintf(lpdi->item.pszText, "%.1f MBit", (float)pRomInfo->RomSize / 0x20000);
break;
case RB_CartridgeID:
strncpy(lpdi->item.pszText, pRomInfo->CartID, lpdi->item.cchTextMax);
break;
case RB_ReleaseVer:
sprintf(lpdi->item.pszText, "v1.%d", pRomInfo->ReleaseVersion);
break;
case RB_SdkVer:
sprintf(lpdi->item.pszText, "v%d.%d%c", pRomInfo->SdkVersion[1] / 10, pRomInfo->SdkVersion[1] % 10, pRomInfo->SdkVersion[0]);
break;
case RB_Manufacturer:
switch (pRomInfo->Manufacturer) {
case 'C':
case 'N':
strncpy(lpdi->item.pszText, "Nintendo", lpdi->item.cchTextMax);
break;
case 0:
strncpy(lpdi->item.pszText, "None", lpdi->item.cchTextMax);
break;
default:
sprintf(lpdi->item.pszText, "(Unknown %c (%X))", pRomInfo->Manufacturer, pRomInfo->Manufacturer);
break;
}
break;
case RB_Country: {
char junk[50];
CountryCodeToString(junk, pRomInfo->Country, 50);
strncpy(lpdi->item.pszText, junk, lpdi->item.cchTextMax);
break;
}
case RB_Crc1:
sprintf(lpdi->item.pszText, "0x%08X", pRomInfo->CRC1);
break;
case RB_Crc2:
sprintf(lpdi->item.pszText, "0x%08X", pRomInfo->CRC2);
break;
case RB_CICChip:
BuildRomCicChipString(pRomInfo->CicChip, lpdi->item.pszText, lpdi->item.cchTextMax, GetRomRegionByCode(pRomInfo->Country));
break;
case RB_UserNotes:
strncpy(lpdi->item.pszText, pRomInfo->UserNotes, lpdi->item.cchTextMax);
break;
case RB_Developer:
strncpy(lpdi->item.pszText, pRomInfo->Developer, lpdi->item.cchTextMax);
break;
case RB_ReleaseDate:
strncpy(lpdi->item.pszText, pRomInfo->ReleaseDate, lpdi->item.cchTextMax);
break;
case RB_Genre:
strncpy(lpdi->item.pszText, pRomInfo->Genre, lpdi->item.cchTextMax);
break;
case RB_Players:
sprintf(lpdi->item.pszText, "%d", pRomInfo->Players);
break;
case RB_ForceFeedback:
strncpy(lpdi->item.pszText, pRomInfo->ForceFeedback, lpdi->item.cchTextMax);
break;
default:
strncpy(lpdi->item.pszText, " ", lpdi->item.cchTextMax);
}
if (lpdi->item.pszText == NULL)
return;
if (strlen(lpdi->item.pszText) == 0)
strcpy(lpdi->item.pszText, " ");
}
void RomList_PopupMenu(LPNMHDR) {
HMENU hMenu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_POPUP));
HMENU hPopupMenu = GetSubMenu(hMenu, 0);
POINT Mouse;
LONG iItem;
GetCursorPos(&Mouse);
iItem = ListView_GetNextItem(hRomList, -1, LVNI_SELECTED);
if (iItem != -1)
strcpy(CurrentRBFileName, RomList.at(iItem).szFullFileName);
else
strcpy(CurrentRBFileName, "");
//Fix up menu
MenuSetText(hPopupMenu, 0, GS(POPUP_PLAY), NULL);
MenuSetText(hPopupMenu, 2, GS(MENU_REFRESH), NULL);
MenuSetText(hPopupMenu, 3, GS(MENU_CHOOSE_ROM), NULL);
MenuSetText(hPopupMenu, 5, GS(POPUP_INFO), NULL);
MenuSetText(hPopupMenu, 6, GS(POPUP_GAMEINFO), NULL);
MenuSetText(hPopupMenu, 8, GS(POPUP_SETTINGS), NULL);
MenuSetText(hPopupMenu, 9, GS(POPUP_CHEATS), NULL);
if (strlen(CurrentRBFileName) == 0) {
DeleteMenu(hPopupMenu, 9, MF_BYPOSITION);
DeleteMenu(hPopupMenu, 8, MF_BYPOSITION);
DeleteMenu(hPopupMenu, 7, MF_BYPOSITION);
DeleteMenu(hPopupMenu, 6, MF_BYPOSITION);
DeleteMenu(hPopupMenu, 5, MF_BYPOSITION);
DeleteMenu(hPopupMenu, 4, MF_BYPOSITION);
DeleteMenu(hPopupMenu, 1, MF_BYPOSITION);
DeleteMenu(hPopupMenu, 0, MF_BYPOSITION);
}
else {
if (BasicMode && !RememberCheats)
DeleteMenu(hPopupMenu, 9, MF_BYPOSITION);
if (BasicMode)
DeleteMenu(hPopupMenu, 8, MF_BYPOSITION);
if (BasicMode && !RememberCheats)
DeleteMenu(hPopupMenu, 7, MF_BYPOSITION);
}
TrackPopupMenu(hPopupMenu, 0, Mouse.x, Mouse.y, 0, hMainWindow, NULL);
DestroyMenu(hMenu);
}
void RomList_SetFocus(void) {
if (!RomListVisible())
return;
SetFocus(hRomList);
}
void RomList_OpenRom(LPNMHDR) {
DWORD ThreadID;
LONG iItem;
iItem = ListView_GetNextItem(hRomList, -1, LVNI_SELECTED);
if (iItem == -1)
return;
strcpy(CurrentFileName, RomList.at(iItem).szFullFileName);
CreateThread(NULL, 0, OpenChosenFile, NULL, 0, &ThreadID);
}
void RomList_SortList(void) {
char SortField[200];
int count, index;
for (count = 0; count < NoOfSortKeys; count++) {
GetSortField(count, SortField, sizeof(SortField));
for (index = 0; index < NoOfFields; index++) {
if (_stricmp(RomBrowserFields[index].Name, SortField) == 0)
break;
}
// Global variables used, sort does not allow passing of variables
gSortFields.Key[count] = index;
gSortFields.KeyAscend[count] = IsSortAscending(count);
}
std::sort(RomList.begin(), RomList.end(), RomList_Compare);
}
void RomListDrawItem(LPDRAWITEMSTRUCT ditem) {
RECT rcItem, rcDraw;
ROM_INFO* pRomInfo;
char String[300];
BOOL bSelected;
HBRUSH hBrush;
LV_COLUMN lvc;
int nColumn;
bSelected = (ListView_GetItemState(hRomList, ditem->itemID, -1) & LVIS_SELECTED);
pRomInfo = &RomList.at(ditem->itemID);
if (bSelected) {
hBrush = CreateSolidBrush(GetColor(pRomInfo->Status, COLOR_HIGHLIGHTED));
SetTextColor(ditem->hDC, GetColor(pRomInfo->Status, COLOR_SELECTED_TEXT));
}
else {
hBrush = GetSysColorBrush(COLOR_WINDOW);
SetTextColor(ditem->hDC, GetColor(pRomInfo->Status, COLOR_TEXT));
}
FillRect(ditem->hDC, &ditem->rcItem, hBrush);
SetBkMode(ditem->hDC, TRANSPARENT);
//Draw
ListView_GetItemRect(hRomList, ditem->itemID, &rcItem, LVIR_LABEL);
ListView_GetItemText(hRomList, ditem->itemID, 0, String, sizeof(String));
memcpy(&rcDraw, &rcItem, sizeof(RECT));
rcDraw.right -= 3;
DrawText(ditem->hDC, String, strlen(String), &rcDraw, DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER);
memset(&lvc, 0, sizeof(lvc));
lvc.mask = LVCF_FMT | LVCF_WIDTH;
for (nColumn = 1; ListView_GetColumn(hRomList, nColumn, &lvc); nColumn += 1) {
rcItem.left = rcItem.right;
rcItem.right += lvc.cx;
ListView_GetItemText(hRomList, ditem->itemID, nColumn, String, sizeof(String));
memcpy(&rcDraw, &rcItem, sizeof(RECT));
rcDraw.right -= 3;
DrawText(ditem->hDC, String, strlen(String), &rcDraw, DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER);
}
DeleteObject(hBrush);
}
void RomListNotify(LPNMHDR pnmh) {
switch (pnmh->code) {
case LVN_GETDISPINFO: {
DWORD wait_result = WaitForSingleObject(gRBMutex, INFINITE);
// Got control of the mutex, that means nothing else is using the listview control
if (wait_result == WAIT_OBJECT_0) {
RomList_GetDispInfo(pnmh);
if (!ReleaseMutex(gRBMutex))
MessageBox(NULL, "Failed to release a mutex???", "Error!", MB_OK);
}
// Abandoned mutex, why???
if (wait_result == WAIT_ABANDONED) {
DisplayError(GS(MSG_MEM_ALLOC_ERROR));
ExitThread(0);
}
}
break;
case LVN_COLUMNCLICK:
RomList_ColumnSortList((LPNMLISTVIEW)pnmh);
break;
case NM_RETURN:
RomList_OpenRom(pnmh);
break;
case NM_DBLCLK:
RomList_OpenRom(pnmh);
break;
case NM_RCLICK:
RomList_PopupMenu(pnmh);
break;
}
}
BOOL RomListVisible(void) {
if (hRomList == NULL)
return FALSE;
return (IsWindowVisible(hRomList));
}
void SaveRomBrowserColumnInfo(void) {
int Column, index;
LV_COLUMN lvColumn;
char String[200], String2[200];
memset(&lvColumn, 0, sizeof(lvColumn));
lvColumn.mask = LVCF_WIDTH;
for (Column = 0; ListView_GetColumn(hRomList, Column, &lvColumn); Column++) {
for (index = 0; index < NoOfFields; index++) {
if (RomBrowserFields[index].Pos == Column)
break;
}
RomBrowserFields[index].ColWidth = lvColumn.cx;
sprintf(String, "%s.Width", RomBrowserFields[index].Name);
sprintf(String2, "%d", lvColumn.cx);
Settings_Write(APPS_NAME, "Rom Browser", String, String2);
}
}
void SaveRomBrowserColumnPosition(int index, int Position) {
char szPos[10];
sprintf(szPos, "%d", Position);
Settings_Write(APPS_NAME, "Rom Browser", RomBrowserFields[index].Name, szPos);
}
void SaveRomList(void) {
char path_buffer[_MAX_PATH], drive[_MAX_DRIVE], dir[_MAX_DIR];
char fname[_MAX_FNAME], ext[_MAX_EXT];
char FileName[_MAX_PATH];
DWORD dwWritten;
HANDLE hFile;
int Size, count;
GetModuleFileName(NULL, path_buffer, sizeof(path_buffer));
_splitpath(path_buffer, drive, dir, fname, ext);
sprintf(FileName, "%s%s%s", drive, dir, ROC_NAME);
hFile = CreateFile(FileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL);
Size = sizeof(ROM_INFO);
// Write the size of ROM_INFO
WriteFile(hFile, &Size, sizeof(Size), &dwWritten, NULL);
// Write the count out
count = (int)RomList.size();
WriteFile(hFile, &count, sizeof(count), &dwWritten, NULL);
// Write out each entry
for (const ROM_INFO &info : RomList) {
WriteFile(hFile, &info, sizeof(info), &dwWritten, NULL);
}
CloseHandle(hFile);
}
// Windows 7 and higher has a bugged SHBrowseForFolder when using BIF_NEWDIALOGSTYLE
// This is a work-around, it enumerates through the child windows until the tree view of the dialog is hit and then
// ensures the selected element is visible.
// This behavior is not apparent in XP or Vista and it is suggested to use the new FolderBrowserDialog
static BOOL CALLBACK EnumCallback(HWND hWndChild, LPARAM)
{
char szClass[MAX_PATH];
HTREEITEM hNode;
if (GetClassName(hWndChild, szClass, sizeof(szClass)) && strcmp(szClass, "SysTreeView32") == 0) {
hNode = TreeView_GetSelection(hWndChild); // found the tree view window
TreeView_EnsureVisible(hWndChild, hNode); // ensure its selection is visible
return(FALSE); // done; stop enumerating
}
return(TRUE); // continue enumerating
}
// This is sort of a hack, it's designed to force SHBrowseForFolder to scroll down to the selected folder
// It seems to not have worked until BFFM_SELCHANGED was included
int CALLBACK SelectRomDirCallBack(HWND hwnd, DWORD uMsg, DWORD, DWORD lpData) {
switch (uMsg)
{
case BFFM_INITIALIZED:
// lpData is TRUE since you are passing a path.
// It would be FALSE if you were passing a pidl.
if (lpData)
SendMessage((HWND)hwnd, BFFM_SETSELECTION, TRUE, lpData);
break;
case BFFM_SELCHANGED:
EnumChildWindows(hwnd, EnumCallback, NULL);
break;
}
return 0;
}
void SelectRomDir(void) {
char Buffer[MAX_PATH], Directory[MAX_PATH], RomDirectory[MAX_PATH + 1];
LPITEMIDLIST pidl;
BROWSEINFO bi = { 0 }; // Initialization to 0 prevents XP crash
Settings_GetDirectory(RomDir, RomDirectory, sizeof(RomDirectory));
bi.hwndOwner = hMainWindow;
bi.pidlRoot = NULL;
bi.pszDisplayName = Buffer;
bi.lpszTitle = GS(SELECT_ROM_DIR);
bi.ulFlags = BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
bi.lpfn = (BFFCALLBACK)SelectRomDirCallBack;
bi.lParam = (DWORD)RomDirectory;
CoInitialize(NULL);
if ((pidl = SHBrowseForFolder(&bi)) != NULL) {
if (SHGetPathFromIDList(pidl, Directory)) {
int len = strlen(Directory);
if (Directory[len - 1] != '\\') {
strcat(Directory, "\\");
}
SetRomDirectory(Directory);
Settings_Write(APPS_NAME, "Directories", "Use Default Rom", "False");
RefreshRomBrowser();
}
}
CoUninitialize();
}
void SetRomBrowserMaximized(BOOL Maximized) {
Settings_Write(APPS_NAME, "Page Setup", "Rom Browser Maximized", Maximized ? STR_TRUE : STR_FALSE);
}
void SetRomBrowserSize(int nWidth, int nHeight) {
char String[100];
sprintf(String, "%d", nWidth);
Settings_Write(APPS_NAME, "Rom Browser Page", "Width", String);
sprintf(String, "%d", nHeight);
Settings_Write(APPS_NAME, "Rom Browser Page", "Height", String);
}
void SetSortAscending(BOOL Ascending, int Index) {
char String[200];
sprintf(String, "Sort Ascending %d", Index);
Settings_Write(APPS_NAME, "Rom Browser Page", String, Ascending ? STR_TRUE : STR_FALSE);
}
void SetSortField(char* FieldName, int Index) {
char String[200];
sprintf(String, "Sort Field %d", Index);
Settings_Write(APPS_NAME, "Rom Browser Page", String, FieldName);
}
void FillRomList(char* Directory) {
char FullPath[MAX_PATH + 1], FileName[MAX_PATH + 1], SearchSpec[MAX_PATH + 1];
char drive[_MAX_DRIVE], dir[_MAX_DIR], ext[_MAX_EXT];
WIN32_FIND_DATA fd;
HANDLE hFind;
strcpy(SearchSpec, Directory);
if (SearchSpec[strlen(Directory) - 1] != '\\')
strcat(SearchSpec, "\\");
strcat(SearchSpec, "*.*");
hFind = FindFirstFile(SearchSpec, &fd);
if (hFind == INVALID_HANDLE_VALUE)
return;
do {
// Force a stop of the scanning
if (!scanning)
break;
if (strcmp(fd.cFileName, ".") == 0 || strcmp(fd.cFileName, "..") == 0)
continue;
strcpy(FullPath, Directory);
if (FullPath[strlen(Directory) - 1] != '\\')
strcat(FullPath, "\\");
strcat(FullPath, fd.cFileName);
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if (Recursion)
FillRomList(FullPath);
continue;
}
_splitpath(FullPath, drive, dir, FileName, ext);
if (_stricmp(ext, ".zip") == 0 || _stricmp(ext, ".v64") == 0 || _stricmp(ext, ".z64") == 0 || _stricmp(ext, ".n64") == 0 ||
_stricmp(ext, ".rom") == 0 || _stricmp(ext, ".jap") == 0 || _stricmp(ext, ".pal") == 0 || _stricmp(ext, ".usa") == 0 ||
_stricmp(ext, ".eur") == 0 || _stricmp(ext, ".bin") == 0) {
AddRomToList(FullPath);
}
} while (FindNextFile(hFind, &fd));
FindClose(hFind);
}
void ShowRomList(HWND hParent) {
DWORD X, Y, Width, Height;
long Style;
int iItem;
if (CPURunning)
return;
if (hRomList != NULL && IsWindowVisible(hRomList))
return;
SetupPlugins(hHiddenWin);
SetupMenu(hMainWindow);
IgnoreMove = TRUE;
ShowWindow(hMainWindow, SW_HIDE);
if (hRomList == NULL)
CreateRomListControl(hParent);
else
EnableWindow(hRomList, TRUE);
if (!GetRomBrowserSize(&Width, &Height)) {
Width = 640;
Height = 480;
}
ChangeWinSize(hMainWindow, Width, Height, NULL);
iItem = ListView_GetNextItem(hRomList, -1, LVNI_SELECTED);
ListView_EnsureVisible(hRomList, iItem, FALSE);
ShowWindow(hRomList, SW_SHOW);
InvalidateRect(hParent, NULL, TRUE);
Style = GetWindowLong(hMainWindow, GWL_STYLE) | WS_SIZEBOX | WS_MAXIMIZEBOX;
SetWindowLong(hMainWindow, GWL_STYLE, Style);
if (!GetStoredWinPos("Main", &X, &Y)) {
X = (GetSystemMetrics(SM_CXSCREEN) - Width) / 2;
Y = (GetSystemMetrics(SM_CYSCREEN) - Height) / 2;
}
SetWindowPos(hMainWindow, HWND_NOTOPMOST, X, Y, 0, 0, SWP_NOSIZE);
if (IsRomBrowserMaximized()) {
ShowWindow(hMainWindow, SW_MAXIMIZE);
}
else {
ShowWindow(hMainWindow, SW_SHOW);
DrawMenuBar(hMainWindow);
ChangeWinSize(hMainWindow, Width, Height, NULL);
}
IgnoreMove = FALSE;
SetupMenu(hMainWindow);
SetFocus(hRomList);
if (pendingUpdate) {
RefreshRomBrowser();
pendingUpdate = FALSE;
}
}
void FreeRomBrowser(void) {
RomList.clear();
ColorList.clear();
}
COLORREF GetColor(char* status, int selection) {
int i = ColorIndex(status);
switch (selection) {
case COLOR_SELECTED_TEXT:
if (i == -1)
return RGB(0xFF, 0xFF, 0xFF);
return ColorList.at(i).SelectedText;
case COLOR_HIGHLIGHTED:
if (i == -1)
return RGB(0, 0, 0);
return ColorList.at(i).HighLight;
default:
if (i == -1)
return RGB(0, 0, 0);
return ColorList.at(i).Text;
}
}
int ColorIndex(char* status) {
for (size_t i = 0; i < ColorList.size(); i++) {
if (strcmp(ColorList.at(i).status_name, status) == 0)
return i;
}
return -1;
}
void SetColors(char* status) {
int count;
COLOR_ENTRY colors;
char String[100], * read;
if (ColorIndex(status) == -1) {
sprintf(String, "%s", status);
Settings_Read(RDS_NAME, "Rom Status", String, "000000", &read);
count = (AsciiToHex(read) & 0xFFFFFF);
if (read)
free(read);
colors.Text = (count & 0x00FF00) | ((count >> 0x10) & 0xFF) | ((count & 0xFF) << 0x10);
sprintf(String, "%s.Sel", status);
Settings_Read(RDS_NAME, "Rom Status", String, "FFFFFF", &read);
count = (AsciiToHex(read) & 0xFFFFFF);
if (read)
free(read);
if (count < 0) {
colors.HighLight = COLOR_HIGHLIGHT + 1;
}
else {
count = (count & 0x00FF00) | ((count >> 0x10) & 0xFF) | ((count & 0xFF) << 0x10);
colors.HighLight = count;
}
sprintf(String, "%s.Seltext", status);
Settings_Read(RDS_NAME, "Rom Status", String, "FFFFFF", &read);
count = (AsciiToHex(read) & 0xFFFFFF);
if (read)
free(read);
colors.SelectedText = (count & 0x00FF00) | ((count >> 0x10) & 0xFF) | ((count & 0xFF) << 0x10);
colors.status_name = (char*)malloc(strlen(status) + 1);
strcpy(colors.status_name, status);
// Save the colors for status
ColorList.push_back(colors);
}
}
LRESULT RomList_FindItem(NMHDR* lParam) {
NMLVFINDITEM* findInfo;
size_t currentPos, startPos;
int col;
findInfo = (NMLVFINDITEM*)lParam;
// Search criteria is not supported, only works with strings for now
if (((findInfo->lvfi.flags) & LVFI_STRING) == 0)
return -1;
// Fetch first column, thats what we are searching by
for (col = 0; col < NoOfFields; col++) {
if (RomBrowserFields[col].Pos == 0)
break;
}
startPos = findInfo->iStart;
// Either the last item was selected or nothing was, start at the top
if (startPos >= RomList.size() - 1 || startPos < 0)
startPos = 0;
currentPos = startPos;
do {
// String insensitive compare, limited to the size of our search criteria in findInfo->lvfi.psz
// So this is a "String starts with, ignore case" match
switch (col) {
case RB_FileName:
if (_tcsnicmp(RomList.at(currentPos).FileName, findInfo->lvfi.psz, strlen(findInfo->lvfi.psz)) == 0)
return currentPos;
break;
case RB_InternalName:
if (_tcsnicmp(RomList.at(currentPos).InternalName, findInfo->lvfi.psz, strlen(findInfo->lvfi.psz)) == 0)
return currentPos;
break;
case RB_GoodName:
if (_tcsnicmp(RomList.at(currentPos).GoodName, findInfo->lvfi.psz, strlen(findInfo->lvfi.psz)) == 0)
return currentPos;
break;
case RB_Status:
if (_tcsnicmp(RomList.at(currentPos).Status, findInfo->lvfi.psz, strlen(findInfo->lvfi.psz)) == 0)
return currentPos;
break;
case RB_CoreNotes:
if (_tcsnicmp(RomList.at(currentPos).CoreNotes, findInfo->lvfi.psz, strlen(findInfo->lvfi.psz)) == 0)
return currentPos;
break;
case RB_PluginNotes:
if (_tcsnicmp(RomList.at(currentPos).PluginNotes, findInfo->lvfi.psz, strlen(findInfo->lvfi.psz)) == 0)
return currentPos;
break;
case RB_UserNotes:
if (_tcsnicmp(RomList.at(currentPos).UserNotes, findInfo->lvfi.psz, strlen(findInfo->lvfi.psz)) == 0)
return currentPos;
break;
case RB_Developer:
if (_tcsnicmp(RomList.at(currentPos).Developer, findInfo->lvfi.psz, strlen(findInfo->lvfi.psz)) == 0)
return currentPos;
break;
case RB_ReleaseDate:
if (_tcsnicmp(RomList.at(currentPos).ReleaseDate, findInfo->lvfi.psz, strlen(findInfo->lvfi.psz)) == 0)
return currentPos;
break;
case RB_Genre:
if (_tcsnicmp(RomList.at(currentPos).Genre, findInfo->lvfi.psz, strlen(findInfo->lvfi.psz)) == 0)
return currentPos;
break;
default:
return -1; // Do not support, this was done because there is no uniqueness on these search results
}
// Start at the top if we've reached the bottom of the list
if (currentPos == RomList.size() - 1)
currentPos = 0;
else
currentPos++;
} while (currentPos != startPos);
// Nothing found
return -1;
}
DWORD WINAPI RefreshRomBrowserMT(LPVOID) {
char RomDirectory[MAX_PATH + 1];
if (scanning)
return 0;
scanning = TRUE;
cancelled = FALSE;
CreateThread(NULL, 0, UpdateBrowser, NULL, 0, NULL);
Settings_GetDirectory(RomDir, RomDirectory, sizeof(RomDirectory));
FillRomList(RomDirectory);
scanning = FALSE;
if (!cancelled) {
RomList_SortList();
ListView_SetItemCount(hRomList, RomList.size());
RomList_SelectFind(LastRoms[0]);
SaveRomList();
}
return 0;
}
// Thread to update the Rom list as it is being loaded
DWORD WINAPI UpdateBrowser(LPVOID) {
DWORD wait_result;
#ifdef SHOW_SCAN_TIME
time_t start = time(NULL), diff;
char msg[100];
#endif
while (scanning) {
wait_result = WaitForSingleObject(gRBMutex, INFINITE);
// Got control of the mutex, that means nothing else is using the listview control
if (wait_result == WAIT_OBJECT_0)
RomList_SortList();
// Abandoned mutex, why???
if (wait_result == WAIT_ABANDONED) {
DisplayError(GS(MSG_MEM_ALLOC_ERROR));
ExitThread(0);
}
// Be sure the mutex is released, as the displaying of items is also going to use the mutex
if (!ReleaseMutex(gRBMutex))
MessageBox(NULL, "Failed to release a mutex???", "Error!", MB_OK);
ListView_SetItemCount(hRomList, RomList.size());
Sleep(250);
}
#ifdef SHOW_SCAN_TIME
diff = time(NULL) - start;
sprintf(msg, "%llu seconds for %u roms", diff, ListView_GetItemCount(hRomList));
MessageBox(NULL, msg, "Time to scan", MB_OK);
#endif
return 0;
}
// Not worrying about size of arrays because both szFullFileName and LastRoms are 261 characters long
// Otherwise I would use a safer compare
void RomList_SelectFind(char* match) {
if (scanning)
return;
for (size_t count = 0; count < RomList.size(); count++) {
if (_stricmp(RomList.at(count).szFullFileName, match) == 0) {
ListView_SetItemState(hRomList, count, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
ListView_EnsureVisible(hRomList, count, FALSE);
break;
}
}
}
void RomList_UpdateSelectedInfo(void) {
int selected;
if (scanning)
return;
selected = ListView_GetNextItem(hRomList, -1, LVNI_SELECTED);
if (selected != -1) {
FillRomExtensionInfo(&RomList.at(selected));
ListView_RedrawItems(hRomList, selected, selected);
}
}
void RomList_StopScanning() {
scanning = FALSE;
cancelled = TRUE;
WaitForSingleObject(romThread, INFINITE);
}