/****************************************************************************** Source File: Code Page Knowledge Base.CPP This implements the code page knowledge base. Copyright (c) 1997 by Microsoft Corporation. All Rights Reserved. A Pretty Penny Enterprises Production. Change History: 02-22-1997 Bob_Kjelgaard@Prodigy.Net Created it ******************************************************************************/ #include "StdAfx.h" //#include #include "Resource.H" #if defined(LONG_NAMES) #include "Code Page Knowledge Base.H" #else #include "CodePage.H" #endif #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif /*** Commented out because this code is no longer in a DLL. static AFX_EXTENSION_MODULE CodePageKnowledgeBaseDLL = { NULL, NULL }; static HINSTANCE hi; extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { if (dwReason == DLL_PROCESS_ATTACH) { hi = hInstance; TRACE0("Code Page Knowledge Base.DLL Initializing!\n"); // Extension DLL one-time initialization AfxInitExtensionModule(CodePageKnowledgeBaseDLL, hInstance); // Insert this DLL into the resource chain new CDynLinkLibrary(CodePageKnowledgeBaseDLL); } else if (dwReason == DLL_PROCESS_DETACH) { TRACE0("Code Page Knowledge Base.DLL Terminating!\n"); } return 1; // ok } */ static CDWordArray cdaInstalled, cdaSupported, cdaMapped; static BOOL CALLBACK EnumProc(LPTSTR lpstrName) { cdaSupported.Add(atoi(lpstrName)); return TRUE; } /****************************************************************************** CCodePageInformation::Load This loads the selected code page into the cache, if it isn't already there. ******************************************************************************/ BOOL CCodePageInformation::Load(DWORD dwidPage) { if (dwidPage == m_dwidMapped) return TRUE; // Already done! if (dwidPage > 65535) // We map words for code pages in civilized lands return FALSE; HRSRC hrsrc = FindResource(AfxGetResourceHandle(), MAKEINTRESOURCE((WORD) dwidPage), MAKEINTRESOURCE(MAPPING_TABLE)); // raid 43537 if (!hrsrc) return FALSE; HGLOBAL hgMap = LoadResource(AfxGetResourceHandle(), hrsrc); if (!hgMap) return FALSE; // This should never happen! LPVOID lpv = LockResource(hgMap); if (!lpv) return FALSE; try { m_cbaMap.RemoveAll(); m_cbaMap.SetSize(SizeofResource(AfxGetResourceHandle(), hrsrc)); memcpy(m_cbaMap.GetData(), lpv, (size_t)m_cbaMap.GetSize()); } catch (CException * pce) { m_dwidMapped = 0; pce -> ReportError(); pce -> Delete(); return FALSE; } m_dwidMapped = dwidPage; return TRUE; } /****************************************************************************** CCodePageInformation::Map This creates either the in or out of unicode translation table, as requested, using the loaded map. ******************************************************************************/ BOOL CCodePageInformation::Map(BOOL bUnicode) { if (!m_dwidMapped) return FALSE; DWORD& dwid = bUnicode ? m_dwidOut : m_dwidIn; if (m_dwidMapped == dwid) return TRUE; struct MB2WCMap { WORD m_wMBCS; WORD m_wWC; } *psMap = (MB2WCMap *) m_cbaMap.GetData(); if (!psMap) return 0; DWORD dwcEntries = (DWORD)m_cbaMap.GetSize() / sizeof *psMap; CWordArray& cwaMap = bUnicode ? m_cwaOut : m_cwaIn; try { cwaMap.RemoveAll(); cwaMap.InsertAt(0, 0xFFFF, 65536); // This is always an invalid value while (dwcEntries--) if (bUnicode) cwaMap[psMap[dwcEntries].m_wWC] = psMap[dwcEntries].m_wMBCS; else cwaMap[psMap[dwcEntries].m_wMBCS] = psMap[dwcEntries].m_wWC; } catch (CException * pce) { dwid = 0; cwaMap.RemoveAll(); pce -> ReportError(); pce -> Delete(); return 0; } dwid = m_dwidMapped; return TRUE; } /****************************************************************************** CCodePageInformation constructor If the statistics haven't been initialized, do it now. Otherwise, this is trivial. ******************************************************************************/ CCodePageInformation::CCodePageInformation() { m_dwidMapped = m_dwidIn = m_dwidOut = 0; // Initialize the statics if we need to. if (cdaInstalled.GetSize()) return; EnumSystemCodePages(&EnumProc, CP_INSTALLED); cdaInstalled.Copy(cdaSupported); cdaSupported.RemoveAll(); EnumSystemCodePages(&EnumProc, CP_SUPPORTED); // Build a list of mappable code pages for (DWORD dw = 400; dw < 32767; dw++) if (HaveMap(dw)) cdaMapped.Add(dw); } const unsigned CCodePageInformation::SupportedCount() const { return (unsigned) cdaSupported.GetSize(); } const unsigned CCodePageInformation::InstalledCount() const { return (unsigned) cdaInstalled.GetSize(); } const unsigned CCodePageInformation::MappedCount() const { return (unsigned) cdaMapped.GetSize(); } const DWORD CCodePageInformation::Supported(unsigned u) const { return cdaSupported[u]; } const DWORD CCodePageInformation::Installed(unsigned u) const { return cdaInstalled[u]; } const DWORD CCodePageInformation::Mapped(unsigned u) const { return cdaMapped[u]; } /****************************************************************************** CCodePageInformation::Mapped(CDWordArray& cdaReturn) Fills the given array with all of the mapped code page IDs. ******************************************************************************/ void CCodePageInformation::Mapped(CDWordArray& cdaReturn) const { cdaReturn.Copy(cdaMapped); } CString CCodePageInformation::Name(DWORD dwidPage) const { CString csTemp; csTemp.LoadString(dwidPage); csTemp.TrimLeft(); csTemp.TrimRight(); if (csTemp.IsEmpty()) csTemp.Format(_TEXT("Code Page %d"), dwidPage); return csTemp; } /****************************************************************************** CCodePageInformation::IsInstalled Rturns true if the font is either installed in the OS or one of our resources. ******************************************************************************/ BOOL CCodePageInformation::IsInstalled(DWORD dwidPage) const { for (unsigned u = 0; u < MappedCount(); u++) if (Mapped(u) == dwidPage) return TRUE; for (u = 0; u < InstalledCount(); u++) if (Installed(u) == dwidPage) return TRUE; return FALSE; } /****************************************************************************** CCodePageInformation::GenerateMap This private member generates a map representing the available one-to-one transformations in an installed code page, and writes it to a file using the code page id to form a unique name ******************************************************************************/ BOOL CCodePageInformation::GenerateMap(DWORD dwidMap) const { // If we can't get Code Page info for it, vanish CPINFO cpi; if (!GetCPInfo(dwidMap, &cpi)) return FALSE; CWordArray cwaMap; for (unsigned u = 0; u < 65536; u++) { unsigned uTo = 0; BOOL bInvalid; int icTo = WideCharToMultiByte(dwidMap, 0, (PWSTR) &u, 1, (PSTR) &uTo, sizeof u, NULL, &bInvalid); if (bInvalid) continue; // Character wasn't any good... _ASSERTE((unsigned) icTo <= cpi.MaxCharSize); // OK, we mapped one- but, before we go on, make sure it also works // in the other direction, since the U2M does some jiggering unsigned u3 = 0; MultiByteToWideChar(dwidMap, 0, (PSTR) &uTo, 2, (PWSTR) &u3, 2); if (u3 != u) continue; // Not a one-for one? Not interested... cwaMap.Add((WORD)uTo); cwaMap.Add((WORD)u); } // OK, we've got the down and dirty details- now, generate the file... try { CString csName; csName.Format(_TEXT("WPS%u.CTT"), dwidMap); CFile cfOut(csName, CFile::modeCreate | CFile::modeWrite | CFile::shareExclusive); // Write the tranlated pairs cfOut.Write(cwaMap.GetData(), (unsigned)(cwaMap.GetSize() * sizeof(WORD))); } catch(CException *pce) { pce -> ReportError(); pce -> Delete(); return FALSE; } return TRUE; } /****************************************************************************** CCodePageInformation::GenerateAllMaps This member will generate an MBCS -> Unicode one-to-one mapping table for all installed code pages in the user's system for which we do not now have maps. ******************************************************************************/ BOOL CCodePageInformation::GenerateAllMaps() const { BOOL bReturn = TRUE; for (unsigned u = InstalledCount(); u--; ) if (!HaveMap(Installed(u)) && !GenerateMap(Installed(u))) bReturn = FALSE; return bReturn; } /****************************************************************************** CCodePageInformation::HaveMap Reports that the map is one of our resources (or isn't, as the case may be). ******************************************************************************/ BOOL CCodePageInformation::HaveMap(DWORD dwidMap) const { return (dwidMap < 65536) ? !!FindResource(AfxGetResourceHandle(), MAKEINTRESOURCE((WORD) dwidMap), MAKEINTRESOURCE(MAPPING_TABLE)) : FALSE; } /****************************************************************************** CCodePageInformation::IsDBCS(DWORD dwidPage) This is actually pretty simple- if the translation table is smaller than 1024 bytes (256 encodings), it isn't DBCS. ******************************************************************************/ BOOL CCodePageInformation::IsDBCS(DWORD dwidPage) { if (!Load(dwidPage)) return FALSE; // May be optimistic, but we'll find out... return m_cbaMap.GetSize() > 1024; } /****************************************************************************** CCodePageInformation::IsDBCS(DWORD dwidPage, WORD wCodePoint) If the page isn't DBCS, we're done. Otherwise, make sure the Unicode->MBCS map is loaded, and get the answer from there. ******************************************************************************/ BOOL CCodePageInformation::IsDBCS(DWORD dwidPage, WORD wCodePoint) { if (!IsDBCS(dwidPage)) return FALSE; if (!Map(TRUE)) return FALSE; // Just say no, because the error's already been told // 0xFFFF is invalid, hence SBCS (default always must be) _ASSERTE(m_cwaOut[wCodePoint] != 0xFFFF); return ((WORD) (1 + m_cwaOut[wCodePoint])) > 0x100; } /****************************************************************************** CCodePageInformation::Convert This is one of the workhorses- it loads the given code page, and maps the given character strings one way or the other, depending upon which is empty. ******************************************************************************/ unsigned CCodePageInformation::Convert(CByteArray& cbaMBCS, CWordArray& cwaWC, DWORD dwidPage){ if (!cbaMBCS.GetSize() == !cwaWC.GetSize()) // Must be clear which way return 0; if (!Load(dwidPage) || !Map((int)cwaWC.GetSize())) return 0; CWordArray& cwaMap = cwaWC.GetSize() ? m_cwaOut : m_cwaIn; try { if (cbaMBCS.GetSize()) { cwaWC.RemoveAll(); for (int i = 0; i < cbaMBCS.GetSize();) { WORD wcThis = cbaMBCS[i]; if (cwaMap[wcThis] == 0xFFFF) { // No SBCS mapping wcThis += cbaMBCS[i + 1] << 8; if (cwaMap[wcThis] == 0xFFFF) { // No DBCS, either? _ASSERTE(FALSE); return 0; // We have failed to convert! } } cwaWC.Add(cwaMap[wcThis]); i += 1 + (wcThis > 0xFF); } } else { cbaMBCS.RemoveAll(); for (int i = 0; i < cwaWC.GetSize(); i++) { if (cwaMap[cwaWC[i]] == 0xFFFF) { _ASSERTE(0); return 0; } cbaMBCS.Add((BYTE) cwaMap[cwaWC[i]]); if (0xFF < cwaMap[cwaWC[i]]) cbaMBCS.Add((BYTE)(cwaMap[cwaWC[i]] >> 8)); } } } catch (CException * pce) { pce -> ReportError(); pce -> Delete(); return 0; } return (unsigned)cwaWC.GetSize(); // Correct conversion count either way! } /****************************************************************************** CCodePageInformation::Collect This member fills a passed CWordArray with either the domain or range of the mapping function. In either case, the array is in ascending order. ******************************************************************************/ BOOL CCodePageInformation::Collect(DWORD dwidPage, CWordArray& cwaCollect, BOOL bUnicode) { if (!Load(dwidPage) || !Map(bUnicode)) return FALSE; CWordArray& cwaMap = bUnicode ? m_cwaOut : m_cwaIn; cwaCollect.RemoveAll(); // Code points < 0x20 always map, but aren't usable, so screen them out try { for (unsigned u = 0x20; u < (unsigned) cwaMap.GetSize(); u++) if (~(int)(short)cwaMap[u]) // 0xFFFF means not mapped! cwaCollect.Add((WORD)u); } catch (CException * pce) { pce -> ReportError(); pce -> Delete(); return FALSE; } return TRUE; }