1 /* 2 * PROJECT: ReactOS Font Shell Extension 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: font list cache handling 5 * COPYRIGHT: Copyright 2019-2021 Mark Jansen <mark.jansen@reactos.org> 6 */ 7 8 #include "precomp.h" 9 10 WINE_DEFAULT_DEBUG_CHANNEL(fontext); 11 12 CFontCache* g_FontCache = NULL; 13 14 CFontInfo::CFontInfo(LPCWSTR name) 15 : m_Name(name) 16 , m_FileRead(false) 17 , m_AttrsRead(false) 18 , m_FileWriteTime({}) 19 , m_dwFileAttributes(0) 20 { 21 m_FileSize.QuadPart = 0; 22 } 23 24 const CStringW& CFontInfo::Name() const 25 { 26 return m_Name; 27 } 28 29 const bool CFontInfo::Valid() const 30 { 31 return !m_Name.IsEmpty(); 32 } 33 34 const CStringW& CFontInfo::File() 35 { 36 if (!m_FileRead) 37 { 38 if (Valid()) 39 { 40 // Read the filename stored in the registry. 41 // This can be either a filename or a full path 42 CRegKey key; 43 if (key.Open(FONT_HIVE, FONT_KEY, KEY_READ) == ERROR_SUCCESS) 44 { 45 CStringW Value; 46 DWORD dwAllocated = 128; 47 LSTATUS Status; 48 do 49 { 50 DWORD dwSize = dwAllocated; 51 PWSTR Buffer = Value.GetBuffer(dwSize); 52 Status = key.QueryStringValue(m_Name, Buffer, &dwSize); 53 Value.ReleaseBuffer(dwSize); 54 if (Status == ERROR_SUCCESS) 55 { 56 // Ensure we do not re-use the same string object, by passing it a PCWSTR 57 m_File = Value.GetString(); 58 break; 59 } 60 dwAllocated += 128; 61 } while (Status == ERROR_MORE_DATA); 62 } 63 } 64 m_FileRead = true; 65 } 66 return m_File; 67 } 68 69 void CFontInfo::ReadAttrs() 70 { 71 CStringW File = g_FontCache->Filename(this, true); 72 73 m_AttrsRead = true; 74 75 WIN32_FIND_DATAW findFileData; 76 HANDLE hFile = FindFirstFileW(File, &findFileData); 77 if (hFile != INVALID_HANDLE_VALUE) 78 { 79 80 // File write time 81 FileTimeToLocalFileTime(&findFileData.ftLastWriteTime, &m_FileWriteTime); 82 83 // File size 84 m_FileSize.HighPart = findFileData.nFileSizeHigh; 85 m_FileSize.LowPart = findFileData.nFileSizeLow; 86 87 m_dwFileAttributes = findFileData.dwFileAttributes; 88 FindClose(hFile); 89 } 90 } 91 92 const LARGE_INTEGER& CFontInfo::FileSize() 93 { 94 if (!m_AttrsRead) 95 ReadAttrs(); 96 97 return m_FileSize; 98 } 99 100 const FILETIME& CFontInfo::FileWriteTime() 101 { 102 if (!m_AttrsRead) 103 ReadAttrs(); 104 105 return m_FileWriteTime; 106 } 107 108 DWORD CFontInfo::FileAttributes() 109 { 110 if (!m_AttrsRead) 111 ReadAttrs(); 112 113 return m_dwFileAttributes; 114 } 115 116 CFontCache::CFontCache() 117 { 118 } 119 120 void CFontCache::SetFontDir(const LPCWSTR Path) 121 { 122 if (m_FontFolderPath.IsEmpty()) 123 m_FontFolderPath = Path; 124 } 125 126 size_t CFontCache::Size() 127 { 128 if (m_Fonts.GetCount() == 0u) 129 Read(); 130 131 return m_Fonts.GetCount(); 132 } 133 134 CStringW CFontCache::Name(size_t Index) 135 { 136 if (m_Fonts.GetCount() == 0u) 137 Read(); 138 139 if (Index >= m_Fonts.GetCount()) 140 return CStringW(); 141 142 return m_Fonts[Index].Name(); 143 } 144 145 CFontInfo* CFontCache::Find(const FontPidlEntry* fontEntry) 146 { 147 if (fontEntry->Index < m_Fonts.GetCount()) 148 { 149 if (m_Fonts[fontEntry->Index].Name().CompareNoCase(fontEntry->Name) == 0) 150 return &m_Fonts[fontEntry->Index]; 151 } 152 153 for (UINT n = 0; n < Size(); ++n) 154 { 155 if (m_Fonts[n].Name().CompareNoCase(fontEntry->Name) == 0) 156 { 157 return &m_Fonts[n]; 158 } 159 } 160 return nullptr; 161 } 162 163 164 CStringW CFontCache::Filename(CFontInfo* info, bool alwaysFullPath) 165 { 166 CStringW File; 167 if (info) 168 { 169 File = info->File(); 170 171 if (!File.IsEmpty() && alwaysFullPath) 172 { 173 // Ensure this is a full path 174 if (PathIsRelativeW(File)) 175 { 176 File = m_FontFolderPath + File; 177 } 178 } 179 } 180 181 return File; 182 } 183 184 void CFontCache::Insert(CAtlList<CFontInfo>& fonts, const CStringW& KeyName) 185 { 186 POSITION it = fonts.GetHeadPosition(); 187 while (it != NULL) 188 { 189 POSITION lastit = it; 190 const CFontInfo& info = fonts.GetNext(it); 191 if (info.Name().CompareNoCase(KeyName) >= 0) 192 { 193 fonts.InsertBefore(lastit, CFontInfo(KeyName)); 194 return; 195 } 196 } 197 fonts.AddTail(CFontInfo(KeyName)); 198 } 199 200 void CFontCache::Read() 201 { 202 CAtlList<CFontInfo> fonts; 203 CRegKey key; 204 205 // Enumerate all registered font names 206 if (key.Open(FONT_HIVE, FONT_KEY, KEY_READ) == ERROR_SUCCESS) 207 { 208 LSTATUS Status; 209 DWORD dwAllocated = 128; 210 DWORD ilIndex = 0; 211 CStringW KeyName; 212 do 213 { 214 DWORD dwSize = dwAllocated; 215 PWSTR Buffer = KeyName.GetBuffer(dwSize); 216 Status = RegEnumValueW(key.m_hKey, ilIndex, Buffer, &dwSize, NULL, NULL, NULL, NULL); 217 KeyName.ReleaseBuffer(dwSize); 218 if (Status == ERROR_SUCCESS) 219 { 220 // Insert will create an ordered list 221 Insert(fonts, KeyName); 222 ilIndex++; 223 continue; 224 } 225 if (Status == ERROR_NO_MORE_ITEMS) 226 break; 227 else if (Status == ERROR_MORE_DATA) 228 { 229 dwAllocated += 128; 230 } 231 } while (Status == ERROR_MORE_DATA || Status == ERROR_SUCCESS); 232 } 233 234 // Move the fonts from a list to an array (for easy indexing) 235 m_Fonts.SetCount(fonts.GetCount()); 236 size_t Index = 0; 237 POSITION it = fonts.GetHeadPosition(); 238 while (it != NULL) 239 { 240 m_Fonts[Index] = fonts.GetNext(it); 241 Index++; 242 } 243 } 244 245