layout: restructure into src/ tests/ android/ scripts/ tools/

- extension/src/{port,platform,codepage} -> src/; native_render -> src/host
  (+ dxt, shaders/); libgr2 -> src/gr2; extension/third_party -> third_party
- extension/tests -> tests/port, libgr2/tests -> tests/gr2
- android-native -> android (build.sh, push-client.sh moved in)
- script -> scripts; tools/40250 -> tools/server; oracle -> tools/granny-oracle;
  perf tools -> tools/perf
- all build trees under build/ (native, release, android, port-gate)
- xrender:: CMake aliases -> mt::; port-map ledger impl paths rewritten

No code changes. ctest 15/15, port_gate macos+android PASS, port_map check 0 errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-29 19:08:19 +09:00
co-authored by Claude Opus 5.5
parent 70710477cf
commit a46093104c
2817 changed files with 13728 additions and 13744 deletions
+350
View File
@@ -0,0 +1,350 @@
#include "Win32Crt.h"
#if !defined(_WIN32)
#include <cctype>
#include <chrono>
#include <cstdio>
#include <cstring>
#include <mutex>
#include <new>
#include <string>
#include <thread>
#include <vector>
// ANSI code pages come from Microsoft's own tables, not the host iconv (bionic has no GBK).
#include "codepage.h"
#include <algorithm>
#include <dirent.h>
#include <fnmatch.h>
#include <sys/stat.h>
#include <unistd.h>
static_assert(sizeof(std::recursive_mutex) <= sizeof(CRITICAL_SECTION::storage), "CRITICAL_SECTION storage");
static_assert(alignof(std::recursive_mutex) <= 16, "CRITICAL_SECTION alignment");
static std::recursive_mutex& cs_mutex(LPCRITICAL_SECTION cs)
{
return *std::launder(reinterpret_cast<std::recursive_mutex*>(cs->storage));
}
void InitializeCriticalSection(LPCRITICAL_SECTION cs) { new (cs->storage) std::recursive_mutex(); }
void DeleteCriticalSection(LPCRITICAL_SECTION cs) { cs_mutex(cs).~recursive_mutex(); }
void EnterCriticalSection(LPCRITICAL_SECTION cs) { cs_mutex(cs).lock(); }
void LeaveCriticalSection(LPCRITICAL_SECTION cs) { cs_mutex(cs).unlock(); }
BOOL CreateDirectory(LPCSTR path, void*)
{
return mkdir(path, 0777) == 0 ? TRUE : FALSE;
}
BOOL DeleteFile(LPCSTR path)
{
return unlink(path) == 0 ? TRUE : FALSE;
}
int _access(const char* path, int mode)
{
int m = F_OK;
if (mode & 2)
m |= W_OK;
if (mode & 4)
m |= R_OK;
return access(path, m);
}
namespace
{
struct FindState
{
std::vector<std::string> paths;
size_t next = 0;
};
void fill_find_data(const std::string& path, WIN32_FIND_DATA* data)
{
const size_t slash = path.find_last_of('/');
const std::string name = path.substr(slash == std::string::npos ? 0 : slash + 1);
std::snprintf(data->cFileName, sizeof(data->cFileName), "%s", name.c_str());
struct stat st{};
data->dwFileAttributes = stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode) ? FILE_ATTRIBUTE_DIRECTORY
: FILE_ATTRIBUTE_NORMAL;
}
}
BOOL CopyFile(LPCSTR existing_path, LPCSTR new_path, BOOL fail_if_exists)
{
if (!existing_path || !new_path)
return FALSE;
if (fail_if_exists && _access(new_path, 0) == 0)
return FALSE;
FILE* in = std::fopen(existing_path, "rb");
if (!in)
return FALSE;
FILE* out = std::fopen(new_path, "wb");
if (!out)
{
std::fclose(in);
return FALSE;
}
char buffer[65536];
size_t n;
bool ok = true;
while ((n = std::fread(buffer, 1, sizeof(buffer), in)) > 0)
ok = ok && std::fwrite(buffer, 1, n, out) == n;
std::fclose(in);
ok = std::fclose(out) == 0 && ok;
return ok ? TRUE : FALSE;
}
HANDLE FindFirstFile(const char* pattern, WIN32_FIND_DATA* data)
{
if (!pattern || !data)
return INVALID_HANDLE_VALUE;
std::string normalized(pattern);
for (char& ch : normalized)
if (ch == '\\') ch = '/';
// Win32 wildcards only appear in the last component; match it against the directory like
// glob(3) would (sorted, '*' skips dot files). glob itself is API 28+ on Android.
std::vector<std::string> paths;
const size_t slash = normalized.rfind('/');
const std::string dir = slash == std::string::npos ? std::string() : normalized.substr(0, slash + 1);
const std::string name = normalized.substr(dir.size());
if (name.find_first_of("*?[") == std::string::npos)
{
struct stat st;
if (::stat(normalized.c_str(), &st) == 0)
paths.push_back(normalized);
}
else if (DIR* d = ::opendir(dir.empty() ? "." : dir.c_str()))
{
while (const dirent* entry = ::readdir(d))
if (::fnmatch(name.c_str(), entry->d_name, FNM_PERIOD) == 0)
paths.push_back(dir + entry->d_name);
::closedir(d);
std::sort(paths.begin(), paths.end());
}
if (paths.empty())
return INVALID_HANDLE_VALUE;
auto* state = new FindState;
state->paths = std::move(paths);
fill_find_data(state->paths[0], data);
state->next = 1;
return state;
}
BOOL FindNextFile(HANDLE handle, WIN32_FIND_DATA* data)
{
if (handle == INVALID_HANDLE_VALUE || !handle || !data)
return FALSE;
auto* state = static_cast<FindState*>(handle);
if (state->next >= state->paths.size())
return FALSE;
fill_find_data(state->paths[state->next++], data);
return TRUE;
}
BOOL FindClose(HANDLE handle)
{
if (handle == INVALID_HANDLE_VALUE || !handle)
return FALSE;
delete static_cast<FindState*>(handle);
return TRUE;
}
void Sleep(DWORD milliseconds)
{
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
}
char* _strlwr(char* s)
{
for (char* p = s; *p; ++p)
*p = static_cast<char>(std::tolower(static_cast<unsigned char>(*p)));
return s;
}
char* _strupr(char* s)
{
for (char* p = s; *p; ++p)
*p = static_cast<char>(std::toupper(static_cast<unsigned char>(*p)));
return s;
}
int _vsnprintf(char* buf, size_t count, const char* fmt, va_list ap)
{
va_list probe;
va_copy(probe, ap);
const int len = std::vsnprintf(nullptr, 0, fmt, probe);
va_end(probe);
if (len < 0)
return -1;
const size_t need = static_cast<size_t>(len);
if (need < count)
return std::vsnprintf(buf, count, fmt, ap);
std::vector<char> full(need + 1);
std::vsnprintf(full.data(), full.size(), fmt, ap);
std::memcpy(buf, full.data(), count);
return need == count ? len : -1;
}
int _snprintf(char* buf, size_t count, const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
const int ret = _vsnprintf(buf, count, fmt, ap);
va_end(ap);
return ret;
}
DWORD timeGetTime()
{
using namespace std::chrono;
const auto ms = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
return static_cast<DWORD>(static_cast<uint64_t>(ms));
}
DWORD GetTickCount()
{
return timeGetTime();
}
namespace {
// CP_ACP is the Western system code page here, as on the 40250 target machines.
UINT ansi(UINT cp) { return cp == CP_ACP ? 1252 : cp; }
bool decode_utf8(const unsigned char* p, size_t n, std::u32string& out)
{
bool ok = true;
for (size_t i = 0; i < n;) {
const unsigned char c = p[i];
size_t len = c < 0x80 ? 1 : c >= 0xC2 && c <= 0xDF ? 2 : c >= 0xE0 && c <= 0xEF ? 3 : c >= 0xF0 && c <= 0xF4 ? 4 : 0;
char32_t cp = len == 1 ? c : len == 2 ? (c & 0x1F) : len == 3 ? (c & 0x0F) : (c & 0x07);
if (len == 0 || i + len > n) { out.push_back(0xFFFD); ok = false; ++i; continue; }
bool valid = true;
for (size_t k = 1; k < len; ++k) {
if ((p[i + k] & 0xC0) != 0x80) { valid = false; break; }
cp = (cp << 6) | (p[i + k] & 0x3F);
}
if (!valid || (len == 3 && cp < 0x800) || (len == 4 && (cp < 0x10000 || cp > 0x10FFFF)) || (cp >= 0xD800 && cp <= 0xDFFF)) {
out.push_back(0xFFFD); ok = false; ++i; continue;
}
out.push_back(cp);
i += len;
}
return ok;
}
void encode_utf8(char32_t cp, std::string& out)
{
if (cp < 0x80) out.push_back(char(cp));
else if (cp < 0x800) { out.push_back(char(0xC0 | (cp >> 6))); out.push_back(char(0x80 | (cp & 0x3F))); }
else if (cp < 0x10000) { out.push_back(char(0xE0 | (cp >> 12))); out.push_back(char(0x80 | ((cp >> 6) & 0x3F))); out.push_back(char(0x80 | (cp & 0x3F))); }
else { out.push_back(char(0xF0 | (cp >> 18))); out.push_back(char(0x80 | ((cp >> 12) & 0x3F))); out.push_back(char(0x80 | ((cp >> 6) & 0x3F))); out.push_back(char(0x80 | (cp & 0x3F))); }
}
bool decode(UINT cp, const unsigned char* p, size_t n, std::u32string& out)
{
if (cp == CP_UTF8) return decode_utf8(p, n, out);
if (mt_codepage::supported(ansi(cp))) return mt_codepage::decode(ansi(cp), p, n, out);
// No table for this code page: ASCII passes, the rest becomes U+FFFD.
for (size_t i = 0; i < n; ++i) out.push_back(p[i] < 0x80 ? char32_t(p[i]) : char32_t(0xFFFD));
return true;
}
void encode(UINT cp, DWORD flags, const std::u32string& in, std::string& out, bool* used_default)
{
if (cp == CP_UTF8) {
for (char32_t c : in) encode_utf8(c, out);
return;
}
if (mt_codepage::supported(ansi(cp))) {
mt_codepage::encode(ansi(cp), in, out, (flags & WC_NO_BEST_FIT_CHARS) == 0, used_default);
return;
}
for (char32_t c : in) {
if (c < 0x80) out.push_back(char(c));
else { out.push_back('?'); *used_default = true; }
}
}
} // namespace
int MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte,
LPWSTR lpWideCharStr, int cchWideChar)
{
if (!lpMultiByteStr || cbMultiByte == 0 || cchWideChar < 0) return 0;
const size_t n = cbMultiByte < 0 ? std::strlen(lpMultiByteStr) + 1 : size_t(cbMultiByte);
std::u32string wide;
const bool ok = decode(CodePage, reinterpret_cast<const unsigned char*>(lpMultiByteStr), n, wide);
if (!ok && (dwFlags & MB_ERR_INVALID_CHARS)) return 0;
if (cchWideChar == 0) return int(wide.size());
if (wide.size() > size_t(cchWideChar)) return 0;
for (size_t i = 0; i < wide.size(); ++i) lpWideCharStr[i] = WCHAR(wide[i]);
return int(wide.size());
}
int WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar,
LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar)
{
if (!lpWideCharStr || cchWideChar == 0 || cbMultiByte < 0) return 0;
size_t n = 0;
if (cchWideChar < 0) { while (lpWideCharStr[n]) ++n; ++n; }
else n = size_t(cchWideChar);
std::u32string wide(n, 0);
for (size_t i = 0; i < n; ++i) wide[i] = char32_t(lpWideCharStr[i]);
std::string bytes;
bool used_default = false;
encode(CodePage, dwFlags, wide, bytes, &used_default);
if (used_default && lpDefaultChar && CodePage != CP_UTF8) {
// '?' was the placeholder; the caller's default char replaces it only where a fallback happened.
std::string redo;
for (char32_t c : wide) {
std::string one;
bool miss = false;
encode(CodePage, dwFlags, std::u32string(1, c), one, &miss);
redo += miss ? std::string(1, *lpDefaultChar) : one;
}
bytes.swap(redo);
}
if (lpUsedDefaultChar) *lpUsedDefaultChar = used_default ? TRUE : FALSE;
if (cbMultiByte == 0) return int(bytes.size());
if (bytes.size() > size_t(cbMultiByte)) return 0;
std::memcpy(lpMultiByteStr, bytes.data(), bytes.size());
return int(bytes.size());
}
BOOL IsDBCSLeadByteEx(UINT CodePage, BYTE TestChar)
{
return mt_codepage::is_lead_byte(ansi(CodePage), TestChar) ? TRUE : FALSE;
}
LPSTR CharNextExA(WORD CodePage, LPCSTR lpCurrentChar, DWORD)
{
if (!*lpCurrentChar) return const_cast<LPSTR>(lpCurrentChar);
if (IsDBCSLeadByteEx(CodePage, BYTE(*lpCurrentChar)) && lpCurrentChar[1]) return const_cast<LPSTR>(lpCurrentChar + 2);
return const_cast<LPSTR>(lpCurrentChar + 1);
}
LPSTR CharPrevExA(WORD CodePage, LPCSTR lpStart, LPCSTR lpCurrentChar, DWORD dwFlags)
{
// Walk forward from lpStart so a trail byte is never taken for a lead byte.
LPCSTR prev = lpStart;
for (LPCSTR p = lpStart; p < lpCurrentChar;) {
prev = p;
LPCSTR next = CharNextExA(CodePage, p, dwFlags);
if (next == p) break;
p = next;
}
return const_cast<LPSTR>(prev);
}
#endif // !_WIN32
int MessageBox(HWND, LPCSTR lpText, LPCSTR lpCaption, UINT)
{
std::fprintf(stderr, "[MessageBox] %s: %s\n", lpCaption ? lpCaption : "", lpText ? lpText : "");
return IDOK;
}