progwrp/locale.c
2025-12-13 23:37:26 +01:00

472 lines
11 KiB
C

#include "progwrp.h"
#include "export.h"
#include "implementations.h"
#include "strsafe.h"
#include "locale.h"
BOOL WINAPI LimitedStrCmp(int StartIdx, int EndIdx, PCWSTR Str0, PCWSTR Str1)
{
int i;
for(i = 0; i < EndIdx - StartIdx; i++)
{
if(Str0[i + StartIdx] != Str1[i])
return FALSE;
if(Str1[i] == '\0' && i < ((EndIdx - StartIdx) - 1))
return FALSE;
}
return TRUE;
}
int WINAPI Implementation_LCIDToLocaleName(
LCID Locale,
LPWSTR lpName,
int cchName,
DWORD dwFlags)
/*
The process of the non-neutral locales will be a little more complex.
There are some that aren't even associated to a neutral locale.
*/
{
int RequiredSize = 0;
size_t Length;
if(!lpName && !cchName)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return 0;
}
if(dwFlags & ~(LOCALE_ALLOW_NEUTRAL_NAMES))
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
if(Locale == LOCALE_INVARIANT || (Locale % 0x400) > LOCALE_NEUTRAL_LIMIT)
{
Locale = 0x409;
}
if(Locale == LOCALE_SYSTEM_DEFAULT)
{
Locale = GetSystemDefaultLCID();
}
if(Locale == LOCALE_USER_DEFAULT)
{
Locale = GetUserDefaultLCID();
}
if ((Locale < LOCALE_NEUTRAL_LIMIT) && (dwFlags & LOCALE_ALLOW_NEUTRAL_NAMES))
{
while(NeutralLangIdTbl[Locale][RequiredSize] != L'\0')
{
++RequiredSize;
}
++RequiredSize;
if(cchName && (cchName < RequiredSize))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return 0;
}
if(cchName && lpName)
StringCchCopyW(lpName, cchName, NeutralLangIdTbl[Locale]);
return RequiredSize;
}
else
{
while(NeutralLangIdTbl[Locale % 0x400][RequiredSize] != L'\0')
{
++RequiredSize;
}
++RequiredSize;
++RequiredSize;
if(StringCchLengthW(LocaleIdentifierTbl[Locale % 0x400][(Locale / 0x400) - 1], STRSAFE_MAX_CCH, &Length) == STRSAFE_E_INVALID_PARAMETER)
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
RequiredSize += Length;
if(cchName && (cchName < RequiredSize))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return 0;
}
if(cchName && lpName)
{
StringCchCopyW(lpName, cchName, NeutralLangIdTbl[Locale % 0x400]);
StringCchCatW(lpName, cchName, L"-");
StringCchCatW(lpName, cchName, LocaleIdentifierTbl[Locale % 0x400][(Locale / 0x400) - 1]);
}
return RequiredSize;
}
// First handle the special locale cases that aren't linked to a neutral locale,
// then handle locales linked to a neutral locale.
// Each LCID pertaining to a particular locale has an offset of 0x400 from the neutral locale.
// e.g. en is 0x9, en-US is 0x409, en-GB is 0x809... ending with 0x3009 for en-ZW (Zimbabwe).
// So all we need to do is build a table for each locale, and derive an index from (LCID / 0x400 - 1) to get the full locale name.
}
LCID WINAPI Implementation_LocaleNameToLCID(
LPCWSTR lpName,
DWORD dwFlags
)
/*
First checks the neutral portion of the locale, which is either two or three characters long.
If it matches an entry in the table, (and it is not a neutral locale outright), then compare
locations.
*/
{
int NeutralLocaleLen = 0, NeutralLocaleIdx, FullLocaleIdx = 0, LocaleLen;
if(dwFlags & ~(LOCALE_ALLOW_NEUTRAL_NAMES) || !lpName)
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
/*
if(lpName == LOCALE_NAME_SYSTEM_DEFAULT)
{
return GetSystemDefaultLCID();
}
*/
if(lpName[0] == L' ' && lpName[1] == L'\0')
{
return 0x409;
}
if(lpName == LOCALE_NAME_USER_DEFAULT)
{
return GetUserDefaultLCID();
}
while(lpName[NeutralLocaleLen] != L'-')
{
++NeutralLocaleLen;
if(lpName[NeutralLocaleLen] == L'\0')
{
if(dwFlags & LOCALE_ALLOW_NEUTRAL_NAMES)
break;
else
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
}
}
for(NeutralLocaleIdx = 0; NeutralLocaleIdx < LOCALE_NEUTRAL_LIMIT; NeutralLocaleIdx++)
{
if(LimitedStrCmp(0, NeutralLocaleLen, lpName, NeutralLangIdTbl[NeutralLocaleIdx]))
break;
}
if(NeutralLocaleIdx == LOCALE_NEUTRAL_LIMIT)
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
else if(dwFlags & LOCALE_ALLOW_NEUTRAL_NAMES)
return NeutralLocaleIdx - 1;
LocaleLen = NeutralLocaleLen + 1;
while(lpName[LocaleLen] != L'\0')
{
++LocaleLen;
}
while(LocaleIdentifierTbl[NeutralLocaleIdx - 1][FullLocaleIdx][0] != L'\0')
{
if(LimitedStrCmp(NeutralLocaleLen + 1, LocaleLen, lpName, LocaleIdentifierTbl[NeutralLocaleIdx - 1][FullLocaleIdx]))
{
return ((FullLocaleIdx + 1) * 0x400) + (NeutralLocaleIdx - 1);
}
++FullLocaleIdx;
}
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
int WINAPI Implementation_ResolveLocaleName(
LPCWSTR lpNameToResolve,
LPWSTR lpLocaleName,
int cchLocaleName
)
/*
This will be part and parcel of my new locale API.
The real implementation of this function was criticized for poor
results at first, so I'll outline what one can expect.
If lpNameToResolve's language label does not match any known neutral locale,
return the user locale.
If lpNameToResolve's language label matches a neutral locale, but there is
no other matching information, return the user locale if it has the same neutral
locale or return the "base locale" for the neutral locale (i.e. en-US for "en-RU"
or "en-BLAHBLAHBLAH").
If there is a valid region identifier at the end of lpNameToResolve (between the last
'-' and null character), in addition to a neutral locale that also has a valid entry
for the region, return the "lan-reg" form.
*/
{
if(lpLocaleName && lpNameToResolve)
{
StringCchCopyW(lpLocaleName, cchLocaleName, lpNameToResolve);
return cchLocaleName;
}
return 0;
}
BOOL WINAPI Implementation_EnumSystemLocalesEx(
LOCALE_ENUMPROCEX lpLocaleEnumProcEx,
DWORD dwFlags,
LPARAM lParam,
LPVOID lpReserved
)
{
return FALSE;
}
int WINAPI Implementation_GetCurrencyFormatEx(
LPCWSTR lpLocaleName,
DWORD dwFlags,
LPCWSTR lpValue,
const CURRENCYFMTW *lpFormat,
LPWSTR lpCurrencyStr,
int cchCurrency
)
{
return GetCurrencyFormatW(Implementation_LocaleNameToLCID(lpLocaleName, 0), dwFlags, lpValue, lpFormat, lpCurrencyStr, cchCurrency);
}
int WINAPI Implementation_GetDateFormatEx(
LPCWSTR lpLocaleName,
DWORD dwFlags,
const SYSTEMTIME *lpDate,
LPCWSTR lpFormat,
LPWSTR lpDateStr,
int cchDate,
LPCWSTR lpCalendar
)
{
return GetDateFormatW(Implementation_LocaleNameToLCID(lpLocaleName, 0), dwFlags, lpDate, lpFormat, lpDateStr, cchDate);
}
DWORD WINAPI Implementation_GetDynamicTimeZoneInformation(
PDYNAMIC_TIME_ZONE_INFORMATION pTimeZoneInformation
)
{
DWORD Result;
TIME_ZONE_INFORMATION tzif;
if(!pTimeZoneInformation)
{
SetLastError(ERROR_INVALID_PARAMETER);
return TIME_ZONE_ID_INVALID;
}
Result = GetTimeZoneInformation(pTimeZoneInformation);
pTimeZoneInformation->TimeZoneKeyName[0] = '\0';
pTimeZoneInformation->DynamicDaylightTimeDisabled = FALSE;
return Result;
}
int WINAPI Implementation_GetTimeFormatEx(
LPCWSTR lpLocaleName,
DWORD dwFlags,
const SYSTEMTIME *lpTime,
LPCWSTR lpFormat,
LPWSTR lpTimeStr,
int cchTime
)
{
return GetTimeFormatW(Implementation_LocaleNameToLCID(lpLocaleName, 0), dwFlags, lpTime, lpFormat, lpTimeStr, cchTime);
}
int WINAPI Implementation_GetLocaleInfoEx(
LPCWSTR lpLocaleName,
LCTYPE LCType,
LPWSTR lpLCData,
int cchData
)
{
return GetLocaleInfoW(Implementation_LocaleNameToLCID(lpLocaleName, 0), LCType, lpLCData, cchData);
}
int WINAPI Implementation_GetNumberFormatEx(
LPCWSTR lpLocaleName,
DWORD dwFlags,
LPCWSTR lpValue,
const NUMBERFMTW *lpFormat,
LPWSTR lpNumberStr,
int cchNumber
)
{
return GetNumberFormatW(Implementation_LocaleNameToLCID(lpLocaleName, 0), dwFlags, lpValue, lpFormat, lpNumberStr, cchNumber);
}
int WINAPI Implementation_GetUserDefaultLocaleName(
LPWSTR lpLocaleName,
int cchLocaleName
)
{
return Implementation_LCIDToLocaleName(GetUserDefaultLCID(), lpLocaleName, cchLocaleName, 0);
}
#define THREAD_LOCALE 1
#define USER_LOCALE 2
#define SYSTEM_LOCALE 4
BOOL WINAPI Implementation_GetThreadPreferredUILanguages(
DWORD dwFlags,
PULONG pulNumLanguages,
PZZWSTR pwszLanguagesBuffer,
PULONG pcchLanguagesBuffer
)
/*
Objective: get the "thread locale" first. Offer the user locale second, and the system locale third.
*/
{
LCID ThreadLocale;
LCID UserLocale;
LCID SystemLocale;
int i;
WCHAR szLocaleName [45];
int szLocaleName_lens [3] = {0, 0, 0};
if(!pulNumLanguages || !pcchLanguagesBuffer)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return 0;
}
*pulNumLanguages = 3;
ThreadLocale = GetThreadLocale();
UserLocale = GetUserDefaultLCID();
SystemLocale = GetSystemDefaultLCID();
if(dwFlags & MUI_LANGUAGE_ID)
{
*pcchLanguagesBuffer = 10;
if(pwszLanguagesBuffer)
{
pwszLanguagesBuffer[0] = HIWORD(ThreadLocale);
pwszLanguagesBuffer[1] = LOWORD(ThreadLocale);
pwszLanguagesBuffer[2] = L'\0';
pwszLanguagesBuffer[3] = HIWORD(UserLocale);
pwszLanguagesBuffer[4] = LOWORD(UserLocale);
pwszLanguagesBuffer[5] = L'\0';
pwszLanguagesBuffer[6] = HIWORD(SystemLocale);
pwszLanguagesBuffer[7] = LOWORD(SystemLocale);
pwszLanguagesBuffer[8] = L'\0';
pwszLanguagesBuffer[9] = L'\0';
}
}
else
{
szLocaleName_lens[0] = Implementation_LCIDToLocaleName(ThreadLocale, szLocaleName, 45, 0);
if(pwszLanguagesBuffer)
{
StringCchCopyW(pwszLanguagesBuffer, *pcchLanguagesBuffer, szLocaleName);
StringCchCatW(pwszLanguagesBuffer, *pcchLanguagesBuffer, L";");
}
szLocaleName_lens[1] = Implementation_LCIDToLocaleName(UserLocale, szLocaleName, 45, 0);
if(pwszLanguagesBuffer)
{
StringCchCatW(pwszLanguagesBuffer, *pcchLanguagesBuffer, szLocaleName);
StringCchCatW(pwszLanguagesBuffer, *pcchLanguagesBuffer, L";");
}
szLocaleName_lens[2] = Implementation_LCIDToLocaleName(SystemLocale, szLocaleName, 45, 0);
if(pwszLanguagesBuffer)
{
StringCchCatW(pwszLanguagesBuffer, *pcchLanguagesBuffer, szLocaleName);
StringCchCatW(pwszLanguagesBuffer, *pcchLanguagesBuffer, L";");
StringCchCatW(pwszLanguagesBuffer, *pcchLanguagesBuffer, L"\0");
for(i = 0; i < *pcchLanguagesBuffer; i++)
{
if(pwszLanguagesBuffer[i] == L';')
pwszLanguagesBuffer[i] = 0;//L'\0';
}
}
*pcchLanguagesBuffer = (szLocaleName_lens[0] + szLocaleName_lens[1] + szLocaleName_lens[2] + 1);
}
return TRUE;
}
int WINAPI Implementation_GetGeoInfoW(
GEOID Location,
GEOTYPE GeoType,
LPWSTR lpGeoData,
int cchData,
LANGID LangId
)
{
return 0;
}
GEOID WINAPI Implementation_GetUserGeoID(
GEOCLASS GeoClass
)
{
return 0x27;
}
BOOL WINAPI Implementation_TzSpecificLocalTimeToSystemTime(
const TIME_ZONE_INFORMATION *lpTimeZoneInformation,
const SYSTEMTIME *lpLocalTime,
LPSYSTEMTIME lpUniversalTime
)
{
return FALSE;
}
int WINAPI Implementation_LCMapStringEx(
LPCWSTR lpLocaleName,
DWORD dwMapFlags,
LPCWSTR lpSrcStr,
int cchSrc,
LPWSTR lpDestStr,
int cchDest,
LPNLSVERSIONINFO lpVersionInformation,
LPVOID lpReserved,
LPARAM sortHandle
)
{
return LCMapStringW(Implementation_LocaleNameToLCID(lpLocaleName, 0), dwMapFlags, lpSrcStr, cchSrc, lpDestStr, cchDest);
}