refactor: clean up Linux_Minecraft and rewrite wstring -> utf8string conversion with simdutf
Some checks are pending
Build (Linux, x86-64) / build-linux (push) Waiting to run
Format Check / clang-format (push) Waiting to run

This commit is contained in:
Tropical 2026-04-02 21:01:25 -05:00
parent 658d72a89e
commit d2d5cd6536
3 changed files with 40 additions and 299 deletions

View file

@ -61,6 +61,7 @@ static void sigsegv_handler(int sig) {
#include "app/linux/Linux_App.h"
#include "app/linux/Linux_UIController.h"
#include "console_helpers/compression.h"
#include "console_helpers/StringHelpers.h"
#include "minecraft/client/Minecraft.h"
#include "minecraft/client/renderer/Tesselator.h"
#include "minecraft/client/renderer/Textures.h"
@ -407,246 +408,6 @@ void DefineActions(void) {
_360_JOY_BUTTON_DPAD_DOWN);
}
#if !defined(__linux__)
HINSTANCE g_hInst = nullptr;
HWND g_hWnd = nullptr;
D3D_DRIVER_TYPE g_driverType = D3D_DRIVER_TYPE_NULL;
D3D_FEATURE_LEVEL g_featureLevel = D3D_FEATURE_LEVEL_11_0;
ID3D11Device* g_pd3dDevice = nullptr;
ID3D11DeviceContext* g_pImmediateContext = nullptr;
IDXGISwapChain* g_pSwapChain = nullptr;
ID3D11RenderTargetView* g_pRenderTargetView = nullptr;
ID3D11DepthStencilView* g_pDepthStencilView = nullptr;
ID3D11Texture2D* g_pDepthStencilBuffer = nullptr;
//
// FUNCTION: WndProc(HWND, uint32_t, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, uint32_t message, WPARAM wParam,
LPARAM lParam) {
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message) {
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId) {
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance) {
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, "Minecraft");
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = "Minecraft";
wcex.lpszClassName = "MinecraftClass";
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassEx(&wcex);
}
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) {
g_hInst = hInstance; // Store instance handle in our global variable
g_hWnd = CreateWindow("MinecraftClass", "Minecraft", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr,
hInstance, nullptr);
if (!g_hWnd) {
return false;
}
ShowWindow(g_hWnd, nCmdShow);
UpdateWindow(g_hWnd);
return true;
}
//--------------------------------------------------------------------------------------
// Create Direct3D device and swap chain
//--------------------------------------------------------------------------------------
int32_t InitDevice() {
int32_t hr = S_OK;
RECT rc;
GetClientRect(g_hWnd, &rc);
uint32_t width = rc.right - rc.left;
uint32_t height = rc.bottom - rc.top;
uint32_t createDeviceFlags = 0;
#if defined(_DEBUG)
createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
D3D_DRIVER_TYPE driverTypes[] = {
D3D_DRIVER_TYPE_HARDWARE,
D3D_DRIVER_TYPE_WARP,
D3D_DRIVER_TYPE_REFERENCE,
};
uint32_t numDriverTypes = ARRAYSIZE(driverTypes);
D3D_FEATURE_LEVEL featureLevels[] = {
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
};
uint32_t numFeatureLevels = ARRAYSIZE(featureLevels);
DXGI_SWAP_CHAIN_DESC sd;
memset(&sd, 0, sizeof(sd));
sd.BufferCount = 1;
sd.BufferDesc.Width = width;
sd.BufferDesc.Height = height;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = g_hWnd;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = true;
for (uint32_t driverTypeIndex = 0; driverTypeIndex < numDriverTypes;
driverTypeIndex++) {
g_driverType = driverTypes[driverTypeIndex];
hr = D3D11CreateDeviceAndSwapChain(
nullptr, g_driverType, nullptr, createDeviceFlags, featureLevels,
numFeatureLevels, D3D11_SDK_VERSION, &sd, &g_pSwapChain,
&g_pd3dDevice, &g_featureLevel, &g_pImmediateContext);
if (HRESULT_SUCCEEDED(hr)) break;
}
if (FAILED(hr)) return hr;
// Create a render target view
ID3D11Texture2D* pBackBuffer = nullptr;
hr = g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
(void**)&pBackBuffer);
if (FAILED(hr)) return hr;
// Create a depth stencil buffer
D3D11_TEXTURE2D_DESC descDepth;
descDepth.Width = width;
descDepth.Height = height;
descDepth.MipLevels = 1;
descDepth.ArraySize = 1;
descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
descDepth.SampleDesc.Count = 1;
descDepth.SampleDesc.Quality = 0;
descDepth.Usage = D3D11_USAGE_DEFAULT;
descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL;
descDepth.CPUAccessFlags = 0;
descDepth.MiscFlags = 0;
hr = g_pd3dDevice->CreateTexture2D(&descDepth, nullptr,
&g_pDepthStencilBuffer);
D3D11_DEPTH_STENCIL_VIEW_DESC descDSView;
descDSView.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
descDSView.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
descDSView.Texture2D.MipSlice = 0;
hr = g_pd3dDevice->CreateDepthStencilView(
g_pDepthStencilBuffer, &descDSView, &g_pDepthStencilView);
hr = g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr,
&g_pRenderTargetView);
pBackBuffer->Release();
if (FAILED(hr)) return hr;
g_pImmediateContext->OMSetRenderTargets(1, &g_pRenderTargetView,
g_pDepthStencilView);
// Setup the viewport
D3D11_VIEWPORT vp;
vp.Width = (float)width;
vp.Height = (float)height;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
g_pImmediateContext->RSSetViewports(1, &vp);
RenderManager.Initialise(g_pd3dDevice, g_pSwapChain);
return 0;
}
//--------------------------------------------------------------------------------------
// Render the frame
//--------------------------------------------------------------------------------------
void Render() {
// Just clear the backbuffer
float ClearColor[4] = {0.0f, 0.125f, 0.3f, 1.0f}; // red,green,blue,alpha
g_pImmediateContext->ClearRenderTargetView(g_pRenderTargetView, ClearColor);
g_pSwapChain->Present(0, 0);
}
//--------------------------------------------------------------------------------------
// Clean up the objects we've created
//--------------------------------------------------------------------------------------
void CleanupDevice() {
if (g_pImmediateContext) g_pImmediateContext->ClearState();
if (g_pRenderTargetView) g_pRenderTargetView->Release();
if (g_pSwapChain) g_pSwapChain->Release();
if (g_pImmediateContext) g_pImmediateContext->Release();
if (g_pd3dDevice) g_pd3dDevice->Release();
}
#endif
int main(int argc, const char* argv[]) {
#if defined(__linux__) && defined(__GLIBC__)
struct sigaction sa;
@ -878,62 +639,11 @@ int main(int argc, const char* argv[]) {
std::vector<uint8_t*> vRichPresenceStrings;
// convert std::wstring to UTF-8 string
// wchar_t is 32bit on all Linux systems, and interpreted as UTF-32
// the code base stores all strings internally as UCS-2 (16bit, subset of
// UTF-16), which, scince it only stores BMP code points, is trivially
// convertable to UTF-32 as well as UTF-16. hence this parser simply parses
// UTF-32
// all implementations of libc (including glibc, musl, uClibc...) implement
// wchar_t as 4byte/32bit (scince around 1999), it would break the libc ABI,
// if this ever will get changed, hence this assert
static_assert(sizeof(wchar_t) == 4, "Linux with non 32bit wchar_t");
std::string wstring_to_utf8(const std::wstring& str) {
std::string result;
// preallocation, so it will never need to resize.
// same allocation size as for the 4byte wstring representation.
// it well get destructed instantly, in the function that it gets called
// from
result.reserve(str.size() * 4);
for (size_t i = 0; i < str.size(); ++i) {
uint32_t cp = static_cast<uint32_t>(str[i]);
// outside of valid unicode range or preserved UTF-16 surrogate pairs
// (just in case)
if (cp > 0x10FFFF || (cp >= 0xD800 && cp <= 0xDFFF)) {
cp = 0xFFFD; // unicode replacement character
}
if (cp < 0x80) {
// ASCII
result += static_cast<char>(cp);
// extract multibyte unicode into multiple bytes of UTF-8
} else if (cp < 0x800) {
result += static_cast<char>(0xC0 | (cp >> 6));
result += static_cast<char>(0x80 | (cp & 0x3F));
} else if (cp < 0x10000) {
result += static_cast<char>(0xE0 | (cp >> 12));
result += static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
result += static_cast<char>(0x80 | (cp & 0x3F));
} else {
result += static_cast<char>(0xF0 | (cp >> 18));
result += static_cast<char>(0x80 | ((cp >> 12) & 0x3F));
result += static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
result += static_cast<char>(0x80 | (cp & 0x3F));
}
}
return result;
}
uint8_t* mallocAndCreateUTF8ArrayFromString(int iID) {
const wchar_t* wchString = app.GetString(iID);
std::wstring srcString = wchString;
std::string dstString = wstring_to_utf8(srcString);
std::u8string dstString = wstring_to_u8string(srcString);
int dst_len = dstString.size() + 1;
uint8_t* strUtf8 = (uint8_t*)malloc(dst_len);

View file

@ -35,6 +35,7 @@ T _fromHEXString(const std::wstring& s) {
std::wstring convStringToWstring(const std::string& converting);
std::wstring u16string_to_wstring(const std::u16string& converting);
std::u16string wstring_to_u16string(const std::wstring& converting);
std::u8string wstring_to_u8string(const std::wstring& converting);
const char* wstringtofilename(const std::wstring& name);
std::wstring filenametowstring(const char* name);

View file

@ -70,7 +70,7 @@ std::wstring u16string_to_wstring(const std::u16string& converting) {
return result;
} else {
static_assert(sizeof(wchar_t) != 2 || sizeof(wchar_t) != 4,
static_assert(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4,
"Here's a nickel, Kid. Go buy yourself a real computer.");
}
}
@ -85,19 +85,49 @@ std::u16string wstring_to_u16string(const std::wstring& converting) {
// POSIX, UTF-32
if (converting.empty()) return {};
const char32_t* data32 =
reinterpret_cast<const char32_t*>(converting.data());
const std::size_t len32 = converting.size();
auto data32 = reinterpret_cast<const char32_t*>(converting.data());
auto len32 = converting.size();
std::u16string result(simdutf::utf16_length_from_utf32(data32, len32),
u'\0');
std::size_t convertedLength =
auto len =
simdutf::convert_utf32_to_utf16(data32, len32, result.data());
result.resize(convertedLength);
result.resize(len);
return result;
} else {
static_assert(sizeof(wchar_t) != 2 || sizeof(wchar_t) != 4,
static_assert(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4,
"Here's a nickel, Kid. Go buy yourself a real computer.");
}
}
std::u8string wstring_to_u8string(const std::wstring& converting) {
if (converting.empty()) return {};
if constexpr (sizeof(wchar_t) == 2) {
auto data16 = reinterpret_cast<const char16_t*>(converting.data());
auto len16 = converting.size();
std::u8string result(simdutf::utf8_length_from_utf16le(data16, len16),
u'\0');
auto len =
simdutf::convert_utf16_to_utf8(data16, len16, reinterpret_cast<char*>(result.data()));
result.resize(len);
return result;
} else if constexpr (sizeof(wchar_t) == 4) {
auto data32 = reinterpret_cast<const char32_t*>(converting.data());
auto len32 = converting.size();
std::u8string result(simdutf::utf8_length_from_utf32(data32, len32),
u'\0');
auto len =
simdutf::convert_utf32_to_utf8(data32, len32, reinterpret_cast<char*>(result.data()));
result.resize(len);
return result;
} else {
static_assert(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4,
"Here's a nickel, Kid. Go buy yourself a real computer.");
}
}