#include "winnls.h" #include "context.h" #include "errors.h" #include "strutil.h" #include #include #include #include #include #include namespace { constexpr DWORD kNormIgnoreCase = 0x00000001; constexpr DWORD LCID_INSTALLED = 0x00000001; constexpr DWORD LCID_SUPPORTED = 0x00000002; constexpr DWORD LCID_ALTERNATE_SORTS = 0x00000004; int compareStrings(const std::string &a, const std::string &b, DWORD dwCmpFlags) { for (size_t i = 0;; ++i) { if (i == a.size()) { if (i == b.size()) { return 2; // CSTR_EQUAL } return 1; // CSTR_LESS_THAN } if (i == b.size()) { return 3; // CSTR_GREATER_THAN } unsigned char c = static_cast(a[i]); unsigned char d = static_cast(b[i]); if (dwCmpFlags & kNormIgnoreCase) { if (c >= 'a' && c <= 'z') { c = static_cast(c - ('a' - 'A')); } if (d >= 'a' && d <= 'z') { d = static_cast(d - ('a' - 'A')); } } if (c != d) { return (c < d) ? 1 : 3; } } } std::string localeInfoString(int LCType) { switch (LCType) { case 4100: // LOCALE_IDEFAULTANSICODEPAGE return "28591"; case 4097: // LOCALE_SENGLANGUAGE return "Lang"; case 4098: // LOCALE_SENGCOUNTRY return "Country"; case 0x1: // LOCALE_ILANGUAGE return "0001"; case 0x15: // LOCALE_SINTLSYMBOL return "Currency"; case 0x14: // LOCALE_SCURRENCY return "sCurrency"; case 0x16: // LOCALE_SMONDECIMALSEP return "."; case 0x17: // LOCALE_SMONTHOUSANDSEP return ","; case 0x18: // LOCALE_SMONGROUPING return ";"; case 0x50: // LOCALE_SPOSITIVESIGN return ""; case 0x51: // LOCALE_SNEGATIVESIGN return "-"; case 0x1A: // LOCALE_IINTLCURRDIGITS case 0x19: // LOCALE_ICURRDIGITS return "2"; default: DEBUG_LOG("STUB: GetLocaleInfo LCType 0x%x not implemented\n", LCType); return ""; } } } // namespace namespace kernel32 { UINT WIN_FUNC GetACP() { HOST_CONTEXT_GUARD(); DEBUG_LOG("GetACP() -> %u\n", 28591); return 28591; // Latin1 (ISO/IEC 8859-1) } LANGID WIN_FUNC GetSystemDefaultLangID() { HOST_CONTEXT_GUARD(); DEBUG_LOG("STUB: GetSystemDefaultLangID()\n"); return 0; } LANGID WIN_FUNC GetUserDefaultUILanguage() { HOST_CONTEXT_GUARD(); DEBUG_LOG("STUB: GetUserDefaultUILanguage()\n"); return 0; } BOOL WIN_FUNC GetCPInfo(UINT CodePage, LPCPINFO lpCPInfo) { HOST_CONTEXT_GUARD(); DEBUG_LOG("GetCPInfo(%u, %p)\n", CodePage, lpCPInfo); (void)CodePage; if (!lpCPInfo) { wibo::lastError = ERROR_INVALID_PARAMETER; return FALSE; } lpCPInfo->MaxCharSize = 1; std::fill(lpCPInfo->DefaultChar, lpCPInfo->DefaultChar + MAX_DEFAULTCHAR, 0); lpCPInfo->DefaultChar[0] = '?'; std::fill(lpCPInfo->LeadByte, lpCPInfo->LeadByte + MAX_LEADBYTES, 0); return TRUE; } int WIN_FUNC CompareStringA(LCID Locale, DWORD dwCmpFlags, LPCSTR lpString1, int cchCount1, LPCSTR lpString2, int cchCount2) { HOST_CONTEXT_GUARD(); DEBUG_LOG("CompareStringA(%u, %u, %s, %d, %s, %d)\n", Locale, dwCmpFlags, lpString1 ? lpString1 : "(null)", cchCount1, lpString2 ? lpString2 : "(null)", cchCount2); (void)Locale; if (!lpString1 || !lpString2) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } if (cchCount1 < 0) { cchCount1 = static_cast(strlen(lpString1)); } if (cchCount2 < 0) { cchCount2 = static_cast(strlen(lpString2)); } std::string str1(lpString1, lpString1 + cchCount1); std::string str2(lpString2, lpString2 + cchCount2); return compareStrings(str1, str2, dwCmpFlags); } int WIN_FUNC CompareStringW(LCID Locale, DWORD dwCmpFlags, LPCWCH lpString1, int cchCount1, LPCWCH lpString2, int cchCount2) { HOST_CONTEXT_GUARD(); DEBUG_LOG("CompareStringW(%u, %u, %p, %d, %p, %d)\n", Locale, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2); (void)Locale; if (!lpString1 || !lpString2) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } std::string str1 = wideStringToString(lpString1, cchCount1); std::string str2 = wideStringToString(lpString2, cchCount2); return compareStrings(str1, str2, dwCmpFlags); } BOOL WIN_FUNC IsValidCodePage(UINT CodePage) { HOST_CONTEXT_GUARD(); DEBUG_LOG("IsValidCodePage(%u)\n", CodePage); (void)CodePage; return TRUE; } BOOL WIN_FUNC IsValidLocale(LCID Locale, DWORD dwFlags) { HOST_CONTEXT_GUARD(); DEBUG_LOG("IsValidLocale(%u, 0x%x)\n", Locale, dwFlags); (void)Locale; if (dwFlags != 0 && (dwFlags & ~(LCID_INSTALLED | LCID_SUPPORTED | LCID_ALTERNATE_SORTS)) != 0) { wibo::lastError = ERROR_INVALID_PARAMETER; return FALSE; } return TRUE; } int WIN_FUNC GetLocaleInfoA(LCID Locale, LCTYPE LCType, LPSTR lpLCData, int cchData) { HOST_CONTEXT_GUARD(); DEBUG_LOG("GetLocaleInfoA(%u, %u, %p, %d)\n", Locale, LCType, lpLCData, cchData); (void)Locale; std::string value = localeInfoString(static_cast(LCType)); size_t required = value.size() + 1; if (cchData == 0) { return static_cast(required); } if (!lpLCData || cchData < 0) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } if (static_cast(cchData) < required) { wibo::lastError = ERROR_INSUFFICIENT_BUFFER; return 0; } std::memcpy(lpLCData, value.c_str(), required); return static_cast(required); } int WIN_FUNC GetLocaleInfoW(LCID Locale, LCTYPE LCType, LPWSTR lpLCData, int cchData) { HOST_CONTEXT_GUARD(); DEBUG_LOG("GetLocaleInfoW(%u, %u, %p, %d)\n", Locale, LCType, lpLCData, cchData); (void)Locale; std::string info = localeInfoString(static_cast(LCType)); auto wide = stringToWideString(info.c_str()); size_t required = wide.size(); if (cchData == 0) { return static_cast(required); } if (!lpLCData || cchData < 0) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } if (static_cast(cchData) < required) { wibo::lastError = ERROR_INSUFFICIENT_BUFFER; return 0; } std::memcpy(lpLCData, wide.data(), required * sizeof(uint16_t)); return static_cast(required); } BOOL WIN_FUNC EnumSystemLocalesA(LOCALE_ENUMPROCA lpLocaleEnumProc, DWORD dwFlags) { HOST_CONTEXT_GUARD(); DEBUG_LOG("EnumSystemLocalesA(%p, 0x%x)\n", lpLocaleEnumProc, dwFlags); (void)dwFlags; if (!lpLocaleEnumProc) { wibo::lastError = ERROR_INVALID_PARAMETER; return FALSE; } char localeId[] = "00000409"; // en-US BOOL callbackResult = lpLocaleEnumProc(localeId); return callbackResult; } LCID WIN_FUNC GetUserDefaultLCID() { HOST_CONTEXT_GUARD(); DEBUG_LOG("GetUserDefaultLCID()\n"); return 0x0409; // en-US } BOOL WIN_FUNC IsDBCSLeadByte(BYTE TestChar) { HOST_CONTEXT_GUARD(); DEBUG_LOG("IsDBCSLeadByte(%u)\n", TestChar); (void)TestChar; return FALSE; } BOOL WIN_FUNC IsDBCSLeadByteEx(UINT CodePage, BYTE TestChar) { HOST_CONTEXT_GUARD(); DEBUG_LOG("IsDBCSLeadByteEx(%u, %u)\n", CodePage, TestChar); auto inRanges = [TestChar](std::initializer_list> ranges) -> BOOL { for (const auto &range : ranges) { if (TestChar >= range.first && TestChar <= range.second) { return TRUE; } } return FALSE; }; wibo::lastError = ERROR_SUCCESS; switch (CodePage) { case 932: // Shift-JIS return inRanges({{0x81, 0x9F}, {0xE0, 0xFC}}); case 936: // GBK case 949: // Korean case 950: // Big5 case 1361: // Johab return inRanges({{0x81, 0xFE}}); case 0: // CP_ACP case 1: // CP_OEMCP case 2: // CP_MACCP case 3: // CP_THREAD_ACP default: return FALSE; } } int WIN_FUNC LCMapStringW(LCID Locale, DWORD dwMapFlags, LPCWCH lpSrcStr, int cchSrc, LPWSTR lpDestStr, int cchDest) { HOST_CONTEXT_GUARD(); DEBUG_LOG("LCMapStringW(%u, 0x%x, %p, %d, %p, %d)\n", Locale, dwMapFlags, lpSrcStr, cchSrc, lpDestStr, cchDest); (void)Locale; if (!lpSrcStr || cchSrc == 0) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } bool nullTerminated = cchSrc < 0; size_t srcLen = nullTerminated ? (wstrlen(lpSrcStr) + 1) : static_cast(cchSrc); if (srcLen == 0) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } if (!lpDestStr || cchDest == 0) { return static_cast(srcLen); } if (cchDest < static_cast(srcLen)) { wibo::lastError = ERROR_INSUFFICIENT_BUFFER; return 0; } if (dwMapFlags & (0x00000400u | 0x00000800u)) { // LCMAP_SORTKEY | LCMAP_BYTEREV DEBUG_LOG("LCMapStringW: unsupported mapping flags 0x%x\n", dwMapFlags); wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } unsigned int casingFlags = dwMapFlags & (0x00000200u | 0x00000100u); // UPPERCASE | LOWERCASE std::vector buffer(srcLen, 0); for (size_t i = 0; i < srcLen; ++i) { uint16_t ch = lpSrcStr[i]; if (casingFlags == 0x00000200u) { buffer[i] = wcharToUpper(ch); } else if (casingFlags == 0x00000100u) { buffer[i] = wcharToLower(ch); } else { buffer[i] = ch; } } std::memcpy(lpDestStr, buffer.data(), srcLen * sizeof(uint16_t)); return static_cast(srcLen); } int WIN_FUNC LCMapStringA(LCID Locale, DWORD dwMapFlags, LPCCH lpSrcStr, int cchSrc, LPSTR lpDestStr, int cchDest) { HOST_CONTEXT_GUARD(); DEBUG_LOG("LCMapStringA(%u, 0x%x, %p, %d, %p, %d)\n", Locale, dwMapFlags, lpSrcStr, cchSrc, lpDestStr, cchDest); if (!lpSrcStr) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } int length = cchSrc; if (length < 0) { length = static_cast(strlen(lpSrcStr)) + 1; } auto wideSrc = stringToWideString(lpSrcStr, static_cast(length)); std::vector wideDest(std::max(cchDest, 0)); int wideResult = LCMapStringW(Locale, dwMapFlags, wideSrc.data(), length, wideDest.empty() ? nullptr : wideDest.data(), cchDest); if (wideResult == 0) { wibo::lastError = ERROR_SUCCESS; return 0; } if (!lpDestStr || cchDest == 0) { return wideResult; } auto mapped = wideStringToString(wideDest.data(), wideResult); size_t bytesToCopy = mapped.size() + 1; if (static_cast(cchDest) < bytesToCopy) { wibo::lastError = ERROR_INSUFFICIENT_BUFFER; return 0; } std::memcpy(lpDestStr, mapped.c_str(), bytesToCopy); return wideResult; } } // namespace kernel32