wibo/dll/kernel32/winnls.cpp
Luke Street 166b9036fd Dont set ERROR_SUCCESS generally
Turns out we should only be setting this in a select few cases.
2025-10-06 00:03:10 -06:00

366 lines
9.9 KiB
C++

#include "winnls.h"
#include "context.h"
#include "errors.h"
#include "strutil.h"
#include <algorithm>
#include <cstring>
#include <cwctype>
#include <initializer_list>
#include <string>
#include <vector>
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<unsigned char>(a[i]);
unsigned char d = static_cast<unsigned char>(b[i]);
if (dwCmpFlags & kNormIgnoreCase) {
if (c >= 'a' && c <= 'z') {
c = static_cast<unsigned char>(c - ('a' - 'A'));
}
if (d >= 'a' && d <= 'z') {
d = static_cast<unsigned char>(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<int>(strlen(lpString1));
}
if (cchCount2 < 0) {
cchCount2 = static_cast<int>(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<int>(LCType));
size_t required = value.size() + 1;
if (cchData == 0) {
return static_cast<int>(required);
}
if (!lpLCData || cchData < 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
if (static_cast<size_t>(cchData) < required) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return 0;
}
std::memcpy(lpLCData, value.c_str(), required);
return static_cast<int>(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<int>(LCType));
auto wide = stringToWideString(info.c_str());
size_t required = wide.size();
if (cchData == 0) {
return static_cast<int>(required);
}
if (!lpLCData || cchData < 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
if (static_cast<size_t>(cchData) < required) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return 0;
}
std::memcpy(lpLCData, wide.data(), required * sizeof(uint16_t));
return static_cast<int>(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<std::pair<uint8_t, uint8_t>> 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<size_t>(cchSrc);
if (srcLen == 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
if (!lpDestStr || cchDest == 0) {
return static_cast<int>(srcLen);
}
if (cchDest < static_cast<int>(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<uint16_t> buffer(srcLen, 0);
for (size_t i = 0; i < srcLen; ++i) {
uint16_t ch = lpSrcStr[i];
if (casingFlags == 0x00000200u) {
buffer[i] = static_cast<uint16_t>(std::towupper(static_cast<wint_t>(ch)));
} else if (casingFlags == 0x00000100u) {
buffer[i] = static_cast<uint16_t>(std::towlower(static_cast<wint_t>(ch)));
} else {
buffer[i] = ch;
}
}
std::memcpy(lpDestStr, buffer.data(), srcLen * sizeof(uint16_t));
return static_cast<int>(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<int>(strlen(lpSrcStr)) + 1;
}
auto wideSrc = stringToWideString(lpSrcStr, static_cast<size_t>(length));
std::vector<uint16_t> 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<size_t>(cchDest) < bytesToCopy) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return 0;
}
std::memcpy(lpDestStr, mapped.c_str(), bytesToCopy);
return wideResult;
}
} // namespace kernel32