2V0-g: port the .fnt font pipeline over a FreeType GDI emulation

CGraphicText, CGraphicFontTexture and CGraphicDib are the 40250 bodies
verbatim; CGraphicTextInstance is verbatim except that its D3D draws become
UI render commands (one image quad per glyph, Bar for cursor/underline).

- platform/Win32Gdi.cpp: the GDI subset those files call, over vendored
  FreeType 2.13.3, following GDI's LOGFONT cell height, win metrics and gasp
  rules; system font lookup with substitute and CJK fallback faces
- Win32Crt: MultiByteToWideChar / WideCharToMultiByte (UTF-8 and 1252 by
  hand, other code pages through iconv)
- CGraphicImageTexture memory textures: glyph pages reach Godot as
  "mem:<id>@<revision>"; Metin2PythonHost.memory_texture + Canvas cache
- Util.cpp code page / font face table, SetDefaultCodePage at boot,
  DefaultFont_* block, fnt resource type, GetMaxTextureWidth/Height
- TextTag, StringCodec, Arabic, Vietnamese copied from 40250
- tests: port.text (Tahoma 12 metrics, alignment, CP949), port.app_loop
  and python_host_test.gd now check glyph quads instead of text commands

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-23 14:17:25 +09:00
co-authored by Claude Opus 5.5
parent 141206cb69
commit afe63bfa61
272 changed files with 137097 additions and 316 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ if(NOT TARGET mtpython)
list(FILTER MT_PLATFORM_SOURCES EXCLUDE REGEX "/ScriptLib/")
endif()
add_library(port_platform STATIC ${MT_PLATFORM_SOURCES})
target_link_libraries(port_platform PUBLIC port_logic)
target_link_libraries(port_platform PUBLIC port_logic PRIVATE mt3p::freetype)
foreach(src IN LISTS MT_PLATFORM_SOURCES)
mt_port_apply_defines(${src})
endforeach()
+76 -32
View File
@@ -1,71 +1,115 @@
// Platform skeleton for EterLib/GrpDIB.h (40250 EterLib/GrpDIB.cpp), generated by platform_stub.py.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
// 40250 EterLib/GrpDIB.cpp, verbatim. The GDI calls are the FreeType-backed platform/Win32Gdi.cpp.
#include "EterLib/StdAfx.h"
#include "EterLib/GrpDIB.h"
#include "../PlatformStub.h"
CGraphicDib::CGraphicDib()
{
MT_PLATFORM_STUB();
Initialize();
}
CGraphicDib::~CGraphicDib()
{
MT_PLATFORM_STUB();
Destroy();
}
auto CGraphicDib::Destroy() -> void
void CGraphicDib::Initialize()
{
MT_PLATFORM_STUB();
m_hDC=NULL;
m_hBmp=NULL;
m_pvBuf=NULL;
m_width=0;
m_height=0;
}
auto CGraphicDib::Create(HDC, int, int) -> bool
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
void CGraphicDib::Destroy()
{
if (m_hBmp) DeleteObject(m_hBmp);
if (m_hDC) DeleteDC(m_hDC);
Initialize();
}
auto CGraphicDib::SetBkMode(int) -> void
bool CGraphicDib::Create(HDC hDC, int width, int height)
{
MT_PLATFORM_STUB();
Destroy();
m_width = width;
m_height = height;
ZeroMemory(&m_bmi.bmiHeader, sizeof(BITMAPINFOHEADER));
m_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
m_bmi.bmiHeader.biWidth = m_width;
m_bmi.bmiHeader.biHeight = -m_height;
m_bmi.bmiHeader.biPlanes = 1;
m_bmi.bmiHeader.biBitCount = 32;
m_bmi.bmiHeader.biCompression = BI_RGB;
m_hDC=CreateCompatibleDC(hDC);
if (!m_hDC)
{
assert(!"CGraphicDib::Create CreateCompatibleDC Error");
return false;
}
m_hBmp=CreateDIBSection(m_hDC, &m_bmi, DIB_RGB_COLORS, &m_pvBuf, NULL, 0);
if (!m_hBmp)
{
assert(!"CGraphicDib::Create CreateDIBSection Error");
return false;
}
SelectObject(m_hDC, m_hBmp);
::SetTextColor(m_hDC, RGB(255, 255, 255));
return true;
}
auto CGraphicDib::TextOut(int, int, const char *) -> void
HDC CGraphicDib::GetDCHandle()
{
MT_PLATFORM_STUB();
return m_hDC;
}
auto CGraphicDib::Put(HDC, int, int) -> void
void CGraphicDib::SetBkMode(int iBkMode)
{
MT_PLATFORM_STUB();
::SetBkMode(m_hDC, iBkMode);
}
auto CGraphicDib::GetWidth() -> int
void CGraphicDib::TextOut(int ix, int iy, const char * c_szText)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<int>();
::SetBkColor(m_hDC, 0);
::TextOut(m_hDC, ix, iy, c_szText, strlen(c_szText));
}
auto CGraphicDib::GetHeight() -> int
void CGraphicDib::Put(HDC hDC, int x, int y)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<int>();
SetDIBitsToDevice(
hDC,
x,
y,
m_width,
m_height,
0,
0,
0,
m_height,
m_pvBuf,
&m_bmi,
DIB_RGB_COLORS
);
}
auto CGraphicDib::GetPointer() -> void *
void* CGraphicDib::GetPointer()
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<void *>();
return m_pvBuf;
}
auto CGraphicDib::GetDCHandle() -> HDC
int CGraphicDib::GetWidth()
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<HDC>();
return m_width;
}
auto CGraphicDib::Initialize() -> void
int CGraphicDib::GetHeight()
{
MT_PLATFORM_STUB();
return m_height;
}
@@ -146,3 +146,17 @@ auto CGraphicDevice::CreateDoublePNTStreamVertexShader() -> DWORD
MT_PLATFORM_STUB();
return mt_platform_stub_return<DWORD>();
}
// 40250 GrpDevice.cpp:298-306; s_MaxTextureWidth/Height come from D3DCAPS8 in __CheckD3DCaps.
// PORT: 4096 is the texture size every Godot renderer on the target GPUs supports.
static DWORD s_MaxTextureWidth = 4096, s_MaxTextureHeight = 4096;
DWORD GetMaxTextureWidth()
{
return s_MaxTextureWidth;
}
DWORD GetMaxTextureHeight()
{
return s_MaxTextureHeight;
}
+267 -41
View File
@@ -1,90 +1,316 @@
// Platform skeleton for EterLib/GrpFontTexture.h (40250 EterLib/GrpFontTexture.cpp), generated by platform_stub.py.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
// 40250 EterLib/GrpFontTexture.cpp, verbatim over the GDI emulation in platform/Win32Gdi.cpp. The
// A4R4G4B4 page textures are CGraphicImageTexture memory textures the Godot canvas samples.
#include "EterLib/StdAfx.h"
#include "EterLib/GrpFontTexture.h"
#include "EterLib/GrpText.h"
#include "EterBase/Stl.h"
#include "../PlatformStub.h"
#include "EterLib/Util.h"
CGraphicFontTexture::CGraphicFontTexture()
{
MT_PLATFORM_STUB();
Initialize();
}
CGraphicFontTexture::~CGraphicFontTexture()
{
MT_PLATFORM_STUB();
Destroy();
}
auto CGraphicFontTexture::Destroy() -> void
void CGraphicFontTexture::Initialize()
{
MT_PLATFORM_STUB();
CGraphicTexture::Initialize();
m_hFontOld = NULL;
m_hFont = NULL;
m_isDirty = false;
m_bItalic = false;
}
auto CGraphicFontTexture::Create(const char *, int, bool) -> bool
bool CGraphicFontTexture::IsEmpty() const
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
return m_fontMap.size() == 0;
}
auto CGraphicFontTexture::CreateDeviceObjects() -> bool
void CGraphicFontTexture::Destroy()
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
HDC hDC = m_dib.GetDCHandle();
if (hDC)
SelectObject(hDC, m_hFontOld);
m_dib.Destroy();
m_lpd3dTexture = NULL;
CGraphicTexture::Destroy();
stl_wipe(m_pFontTextureVector);
m_charInfoMap.clear();
if (m_fontMap.size())
{
TFontMap::iterator i = m_fontMap.begin();
while(i != m_fontMap.end())
{
DeleteObject((HGDIOBJ)i->second);
++i;
}
m_fontMap.clear();
}
Initialize();
}
auto CGraphicFontTexture::DestroyDeviceObjects() -> void
bool CGraphicFontTexture::CreateDeviceObjects()
{
MT_PLATFORM_STUB();
return true;
}
auto CGraphicFontTexture::CheckTextureIndex(DWORD) -> bool
void CGraphicFontTexture::DestroyDeviceObjects()
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
}
auto CGraphicFontTexture::SelectTexture(DWORD) -> void
bool CGraphicFontTexture::Create(const char* c_szFontName, int fontSize, bool bItalic)
{
MT_PLATFORM_STUB();
Destroy();
strncpy(m_fontName, c_szFontName, sizeof(m_fontName)-1);
m_fontSize = fontSize;
m_bItalic = bItalic;
m_x = 0;
m_y = 0;
m_step = 0;
DWORD width = 256,height = 256;
if (GetMaxTextureWidth() > 512)
width = 512;
if (GetMaxTextureHeight() > 512)
height = 512;
if (!m_dib.Create(ms_hDC, width, height))
return false;
HDC hDC = m_dib.GetDCHandle();
m_hFont = GetFont(GetDefaultCodePage());
m_hFontOld=(HFONT)SelectObject(hDC, m_hFont);
SetTextColor(hDC, RGB(255, 255, 255));
SetBkColor(hDC, 0);
if (!AppendTexture())
return false;
return true;
}
auto CGraphicFontTexture::UpdateTexture() -> bool
HFONT CGraphicFontTexture::GetFont(WORD codePage)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
HFONT hFont = NULL;
TFontMap::iterator i = m_fontMap.find(codePage);
if(i != m_fontMap.end())
{
hFont = i->second;
}
else
{
LOGFONT logFont;
memset(&logFont, 0, sizeof(LOGFONT));
logFont.lfHeight = m_fontSize;
logFont.lfEscapement = 0;
logFont.lfOrientation = 0;
logFont.lfWeight = FW_NORMAL;
logFont.lfItalic = (BYTE) m_bItalic;
logFont.lfUnderline = FALSE;
logFont.lfStrikeOut = FALSE;
logFont.lfCharSet = GetCharsetFromCodePage(codePage);
logFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
logFont.lfQuality = ANTIALIASED_QUALITY;
logFont.lfPitchAndFamily = DEFAULT_PITCH;
//Tracenf("font: %s", GetFontFaceFromCodePage(codePage));
strcpy(logFont.lfFaceName, m_fontName); //GetFontFaceFromCodePage(codePage));
//strcpy(logFont.lfFaceName, GetFontFaceFromCodePage(codePage));
hFont = CreateFontIndirect(&logFont);
m_fontMap.insert(TFontMap::value_type(codePage, hFont));
}
return hFont;
}
auto CGraphicFontTexture::GetCharacterInfomation(WORD, wchar_t) -> TCharacterInfomation *
bool CGraphicFontTexture::AppendTexture()
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<TCharacterInfomation *>();
CGraphicImageTexture * pNewTexture = new CGraphicImageTexture;
if (!pNewTexture->Create(m_dib.GetWidth(), m_dib.GetHeight(), D3DFMT_A4R4G4B4))
{
delete pNewTexture;
return false;
}
m_pFontTextureVector.push_back(pNewTexture);
return true;
}
auto CGraphicFontTexture::UpdateCharacterInfomation(TCharacterKey) -> TCharacterInfomation *
bool CGraphicFontTexture::UpdateTexture()
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<TCharacterInfomation *>();
if(!m_isDirty)
return true;
m_isDirty = false;
CGraphicImageTexture * pFontTexture = m_pFontTextureVector.back();
if (!pFontTexture)
return false;
WORD* pwDst;
int pitch;
if (!pFontTexture->Lock(&pitch, (void**)&pwDst))
return false;
pitch /= 2;
int width = m_dib.GetWidth();
int height = m_dib.GetHeight();
DWORD * pdwSrc = (DWORD*)m_dib.GetPointer();
for (int y = 0; y < height; ++y, pwDst += pitch, pdwSrc += width)
for (int x = 0; x < width; ++x)
pwDst[x]=pdwSrc[x];
pFontTexture->Unlock();
return true;
}
auto CGraphicFontTexture::IsEmpty() const -> bool
CGraphicFontTexture::TCharacterInfomation* CGraphicFontTexture::GetCharacterInfomation(WORD codePage, wchar_t keyValue)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
TCharacterKey code(codePage, keyValue);
TCharacterInfomationMap::iterator f = m_charInfoMap.find(code);
if (m_charInfoMap.end() == f)
{
return UpdateCharacterInfomation(code);
}
else
{
return &f->second;
}
}
auto CGraphicFontTexture::Initialize() -> void
CGraphicFontTexture::TCharacterInfomation* CGraphicFontTexture::UpdateCharacterInfomation(TCharacterKey code)
{
MT_PLATFORM_STUB();
HDC hDC = m_dib.GetDCHandle();
SelectObject(hDC, GetFont(code.first));
wchar_t keyValue = code.second;
if (keyValue == 0x08)
keyValue = L' '; // 탭은 공백으로 바꾼다 (아랍 출력시 탭 사용: NAME:\tTEXT -> TEXT\t:NAME 로 전환됨 )
ABCFLOAT stABC;
SIZE size;
if (!GetTextExtentPoint32W(hDC, &keyValue, 1, &size) || !GetCharABCWidthsFloatW(hDC, keyValue, keyValue, &stABC))
return NULL;
size.cx = stABC.abcfB;
if( stABC.abcfA > 0.0f )
size.cx += ceilf(stABC.abcfA);
if( stABC.abcfC > 0.0f )
size.cx += ceilf(stABC.abcfC);
size.cx++;
LONG lAdvance = ceilf( stABC.abcfA + stABC.abcfB + stABC.abcfC );
int width = m_dib.GetWidth();
int height = m_dib.GetHeight();
if (m_x + size.cx >= (width - 1))
{
m_y += (m_step + 1);
m_step = 0;
m_x = 0;
if (m_y + size.cy >= (height - 1))
{
if (!UpdateTexture())
{
return NULL;
}
if (!AppendTexture())
return NULL;
m_y = 0;
}
}
TextOutW(hDC, m_x, m_y, &keyValue, 1);
int nChrX;
int nChrY;
int nChrWidth = size.cx;
int nChrHeight = size.cy;
int nDIBWidth = m_dib.GetWidth();
DWORD*pdwDIBData=(DWORD*)m_dib.GetPointer();
DWORD*pdwDIBBase=pdwDIBData+nDIBWidth*m_y+m_x;
DWORD*pdwDIBRow;
pdwDIBRow=pdwDIBBase;
for (nChrY=0; nChrY<nChrHeight; ++nChrY, pdwDIBRow+=nDIBWidth)
{
for (nChrX=0; nChrX<nChrWidth; ++nChrX)
{
pdwDIBRow[nChrX]=(pdwDIBRow[nChrX]&0xff) ? 0xffff : 0;
}
}
float rhwidth = 1.0f / float(width);
float rhheight = 1.0f / float(height);
TCharacterInfomation& rNewCharInfo = m_charInfoMap[code];
rNewCharInfo.index = static_cast<short>(m_pFontTextureVector.size() - 1);
rNewCharInfo.width = size.cx;
rNewCharInfo.height = size.cy;
rNewCharInfo.left = float(m_x) * rhwidth;
rNewCharInfo.top = float(m_y) * rhheight;
rNewCharInfo.right = float(m_x+size.cx) * rhwidth;
rNewCharInfo.bottom = float(m_y+size.cy) * rhheight;
rNewCharInfo.advance = (float) lAdvance;
m_x += size.cx;
if (m_step < size.cy)
m_step = size.cy;
m_isDirty = true;
return &rNewCharInfo;
}
auto CGraphicFontTexture::AppendTexture() -> bool
bool CGraphicFontTexture::CheckTextureIndex(DWORD dwTexture)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
if (dwTexture >= m_pFontTextureVector.size())
return false;
return true;
}
auto CGraphicFontTexture::GetFont(WORD) -> HFONT
void CGraphicFontTexture::SelectTexture(DWORD dwTexture)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<HFONT>();
assert(CheckTextureIndex(dwTexture));
m_lpd3dTexture = m_pFontTextureVector[dwTexture]->GetD3DTexture();
}
@@ -4,7 +4,11 @@
#include "EterLib/GrpImageTexture.h"
#include "../PlatformStub.h"
#include "UIRenderCommands.h"
#include <cstring>
#include <map>
#include <mutex>
namespace {
unsigned little16(const unsigned char* data) { return data[0] | (unsigned(data[1]) << 8); }
@@ -36,33 +40,150 @@ bool jpeg_size(const unsigned char* data, unsigned size, unsigned* width, unsign
}
return false;
}
// PORT: the IDirect3DTexture8 behind a CGraphicImageTexture::Create texture. Lock hands out the
// pixels in the texture's own format (40250 writes A4R4G4B4 words into the font pages); Unlock
// converts them to ARGB32 for the canvas and bumps the revision in the texture's name.
struct MemoryTexture : IDirect3DTexture8
{
unsigned id = 0;
int width = 0;
int height = 0;
D3DFORMAT format = D3DFMT_UNKNOWN;
std::vector<unsigned char> native;
std::vector<std::uint32_t> argb;
std::uint32_t revision = 0;
};
std::mutex g_memory_mutex;
std::map<unsigned, MemoryTexture*> g_memory;
unsigned g_memory_next_id = 1;
int bytes_per_pixel(D3DFORMAT format)
{
switch (format) {
case D3DFMT_A4R4G4B4: case D3DFMT_R5G6B5: case D3DFMT_A1R5G5B5: case D3DFMT_X1R5G5B5: case D3DFMT_X4R4G4B4:
return 2;
default:
return 4;
}
}
std::uint32_t expand(unsigned value, int bits)
{
return bits == 4 ? value * 0x11 : bits == 5 ? (value << 3) | (value >> 2) : bits == 6 ? (value << 2) | (value >> 4) : value;
}
std::uint32_t native_to_argb(D3DFORMAT format, const unsigned char* p)
{
const unsigned w = p[0] | (unsigned(p[1]) << 8);
switch (format) {
case D3DFMT_A4R4G4B4:
return (expand(w >> 12, 4) << 24) | (expand((w >> 8) & 15, 4) << 16) | (expand((w >> 4) & 15, 4) << 8) | expand(w & 15, 4);
case D3DFMT_X4R4G4B4:
return 0xFF000000u | (expand((w >> 8) & 15, 4) << 16) | (expand((w >> 4) & 15, 4) << 8) | expand(w & 15, 4);
case D3DFMT_R5G6B5:
return 0xFF000000u | (expand(w >> 11, 5) << 16) | (expand((w >> 5) & 63, 6) << 8) | expand(w & 31, 5);
case D3DFMT_A1R5G5B5: case D3DFMT_X1R5G5B5:
return ((format == D3DFMT_X1R5G5B5 || (w & 0x8000)) ? 0xFF000000u : 0) | (expand((w >> 10) & 31, 5) << 16) |
(expand((w >> 5) & 31, 5) << 8) | expand(w & 31, 5);
case D3DFMT_X8R8G8B8:
return 0xFF000000u | (p[2] << 16) | (p[1] << 8) | p[0];
default:
return (std::uint32_t(p[3]) << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
}
}
MemoryTexture* live_memory_texture(const IDirect3DTexture8* texture)
{
for (const auto& entry : g_memory)
if (entry.second == texture) return entry.second;
return nullptr;
}
}
void UIRenderReleaseMemoryTexture(IDirect3DTexture8* texture)
{
std::lock_guard<std::mutex> lock(g_memory_mutex);
if (MemoryTexture* memory = live_memory_texture(texture)) {
g_memory.erase(memory->id);
delete memory;
}
}
bool UIRenderMemoryTexture(const std::string& name, UIMemoryTexture* out)
{
if (name.compare(0, 4, "mem:") != 0 || !out) return false;
const unsigned id = unsigned(std::strtoul(name.c_str() + 4, nullptr, 10));
std::lock_guard<std::mutex> lock(g_memory_mutex);
auto it = g_memory.find(id);
if (it == g_memory.end()) return false;
out->width = it->second->width;
out->height = it->second->height;
out->revision = it->second->revision;
out->argb = it->second->argb;
return true;
}
CGraphicImageTexture::CGraphicImageTexture()
{
MT_PLATFORM_STUB();
Initialize();
}
CGraphicImageTexture::~CGraphicImageTexture()
{
MT_PLATFORM_STUB();
Destroy();
}
auto CGraphicImageTexture::Destroy() -> void
{
CGraphicTexture::Destroy();
Initialize();
}
auto CGraphicImageTexture::Create(UINT, UINT, D3DFORMAT, DWORD) -> bool
auto CGraphicImageTexture::Create(UINT width, UINT height, D3DFORMAT d3dFmt, DWORD dwFilter) -> bool
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
Destroy();
m_width = width;
m_height = height;
m_d3dFmt = d3dFmt;
m_dwFilter = dwFilter;
return CreateDeviceObjects();
}
auto CGraphicImageTexture::CreateDeviceObjects() -> bool
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
if (m_stFileName.empty())
{
// PORT: ms_lpd3dDevice->CreateTexture(m_width, m_height, 1, 0, m_d3dFmt, D3DPOOL_MANAGED) becomes a
// memory texture the canvas fetches by name.
if (m_width <= 0 || m_height <= 0)
return false;
auto* memory = new MemoryTexture();
memory->width = m_width;
memory->height = m_height;
memory->format = m_d3dFmt;
memory->native.assign(size_t(m_width) * size_t(m_height) * size_t(bytes_per_pixel(m_d3dFmt)), 0);
memory->argb.assign(size_t(m_width) * size_t(m_height), 0);
{
std::lock_guard<std::mutex> lock(g_memory_mutex);
memory->id = g_memory_next_id++;
g_memory[memory->id] = memory;
}
m_lpd3dTexture = memory;
}
else
{
// PORT: file textures are decoded by the canvas from the pack path; only their size is read here
// (CreateFromMemoryFile), so there is no device object to recreate.
return !m_bEmpty;
}
m_bEmpty = false;
return true;
}
auto CGraphicImageTexture::CreateFromTexturePointer(const CGraphicTexture *source) -> void
@@ -117,9 +238,16 @@ auto CGraphicImageTexture::SetFileName(const char *name) -> void
}
// PORT: where 40250 binds pTexture->GetD3DTexture(), the canvas gets the pack path of the file the
// texture was decoded from (m_stFileName is protected, hence the derived accessor).
// texture was decoded from (m_stFileName is protected, hence the derived accessor), or the name of the
// memory texture bound to it (a CGraphicFontTexture after SelectTexture).
std::string UIRenderTextureName(const CGraphicTexture* texture)
{
if (!texture) return {};
if (texture->GetD3DTexture()) {
std::lock_guard<std::mutex> lock(g_memory_mutex);
if (const MemoryTexture* memory = live_memory_texture(texture->GetD3DTexture()))
return "mem:" + std::to_string(memory->id) + "@" + std::to_string(memory->revision);
}
struct Access : CGraphicImageTexture {
static const std::string& name(const CGraphicImageTexture* t) { return t->*(&Access::m_stFileName); }
};
@@ -127,18 +255,38 @@ std::string UIRenderTextureName(const CGraphicTexture* texture)
return image ? Access::name(image) : std::string();
}
auto CGraphicImageTexture::Lock(int *, void **, int) -> bool
// PORT: LockRect/UnlockRect on the memory texture (one level; the lock is on the whole surface).
auto CGraphicImageTexture::Lock(int* pRetPitch, void** ppRetPixels, int) -> bool
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
std::lock_guard<std::mutex> lock(g_memory_mutex);
MemoryTexture* memory = live_memory_texture(m_lpd3dTexture);
if (!memory)
return false;
*pRetPitch = memory->width * bytes_per_pixel(memory->format);
*ppRetPixels = memory->native.data();
return true;
}
auto CGraphicImageTexture::Unlock(int) -> void
{
MT_PLATFORM_STUB();
std::lock_guard<std::mutex> lock(g_memory_mutex);
MemoryTexture* memory = live_memory_texture(m_lpd3dTexture);
assert(memory != NULL);
if (!memory)
return;
const int bpp = bytes_per_pixel(memory->format);
for (size_t i = 0; i < memory->argb.size(); ++i)
memory->argb[i] = native_to_argb(memory->format, memory->native.data() + i * size_t(bpp));
++memory->revision;
}
auto CGraphicImageTexture::Initialize() -> void
{
MT_PLATFORM_STUB();
CGraphicTexture::Initialize();
m_stFileName = "";
m_d3dFmt=D3DFMT_UNKNOWN;
m_dwFilter=0;
}
+72 -32
View File
@@ -1,62 +1,102 @@
// Platform skeleton for EterLib/GrpText.h (40250 EterLib/GrpText.cpp), generated by platform_stub.py.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
// 40250 EterLib/GrpText.cpp, verbatim.
#include "EterLib/StdAfx.h"
#include "EterBase/Utils.h"
#include "EterLib/GrpText.h"
#include "../PlatformStub.h"
auto CGraphicText::Type() -> TType
CGraphicText::CGraphicText(const char* c_szFileName) : CResource(c_szFileName)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<TType>();
}
CGraphicText::CGraphicText(const char * c_szFileName) : CResource(c_szFileName) // initializers as in 40250
{
MT_PLATFORM_STUB();
}
CGraphicText::~CGraphicText()
{
MT_PLATFORM_STUB();
}
auto CGraphicText::CreateDeviceObjects() -> bool
bool CGraphicText::CreateDeviceObjects()
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
return m_fontTexture.CreateDeviceObjects();
}
auto CGraphicText::DestroyDeviceObjects() -> void
void CGraphicText::DestroyDeviceObjects()
{
MT_PLATFORM_STUB();
m_fontTexture.DestroyDeviceObjects();
}
auto CGraphicText::GetFontTexturePointer() -> CGraphicFontTexture *
CGraphicFontTexture* CGraphicText::GetFontTexturePointer()
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<CGraphicFontTexture *>();
return &m_fontTexture;
}
auto CGraphicText::OnLoad(int, const void *) -> bool
CGraphicText::TType CGraphicText::Type()
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
static TType s_type = StringToType("CGraphicText");
return s_type;
}
auto CGraphicText::OnClear() -> void
bool CGraphicText::OnLoad(int /*iSize*/, const void* /*c_pvBuf*/)
{
MT_PLATFORM_STUB();
static char strName[32];
int size;
bool bItalic = false;
// format
// 굴림.fnt "굴림" 폰트 기본 사이즈 12 로 로딩
// 굴림:18.fnt "굴림" 폰트 사이즈 18 로 로딩
// 굴림:14i.fnt "굴림" 폰트 사이즈 14 & 이탤릭으로 로딩
const char * p = strrchr(GetFileName(), ':');
if (p)
{
strncpy(strName, GetFileName(), MIN(31, p - GetFileName()));
++p;
static char num[8];
int i = 0;
while (*p && isdigit(*p))
{
num[i++] = *(p++);
}
num[i] = '\0';
if(*p == 'i')
bItalic = true;
size = atoi(num);
}
else
{
p = strrchr(GetFileName(), '.');
if (!p)
{
assert(!"CGraphicText::OnLoadFromFile there is no extension (ie: .fnt)");
strName[0] = '\0';
}
else
strncpy(strName, GetFileName(), MIN(31, p - GetFileName()));
size = 12;
}
if (!m_fontTexture.Create(strName, size, bItalic))
return false;
return true;
}
auto CGraphicText::OnIsEmpty() const -> bool
void CGraphicText::OnClear()
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
m_fontTexture.Destroy();
}
auto CGraphicText::OnIsType(TType) -> bool
bool CGraphicText::OnIsEmpty() const
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
return m_fontTexture.IsEmpty();
}
bool CGraphicText::OnIsType(TType type)
{
if (CGraphicText::Type() == type)
return true;
return CResource::OnIsType(type);
}
File diff suppressed because it is too large Load Diff
+14 -10
View File
@@ -4,6 +4,7 @@
#include "EterLib/GrpTexture.h"
#include "../PlatformStub.h"
#include "UIRenderCommands.h"
auto CGraphicTexture::IsEmpty() const -> bool
{
@@ -27,34 +28,37 @@ auto CGraphicTexture::SetTextureStage(int) const -> void
auto CGraphicTexture::GetD3DTexture() const -> LPDIRECT3DTEXTURE8
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<LPDIRECT3DTEXTURE8>();
return m_lpd3dTexture;
}
// PORT: safe_release(m_lpd3dTexture); the only textures this platform creates are memory textures.
auto CGraphicTexture::DestroyDeviceObjects() -> void
{
MT_PLATFORM_STUB();
if (m_lpd3dTexture)
UIRenderReleaseMemoryTexture(m_lpd3dTexture);
m_lpd3dTexture = NULL;
}
CGraphicTexture::CGraphicTexture()
{
m_bEmpty = true;
m_width = m_height = 0;
m_lpd3dTexture = nullptr;
Initialize();
}
CGraphicTexture::~CGraphicTexture()
{
MT_PLATFORM_STUB();
}
auto CGraphicTexture::Destroy() -> void
{
m_bEmpty = true;
m_width = m_height = 0;
DestroyDeviceObjects();
Initialize();
}
auto CGraphicTexture::Initialize() -> void
{
MT_PLATFORM_STUB();
m_lpd3dTexture = NULL;
m_width = 0;
m_height = 0;
m_bEmpty = true;
}
@@ -30,8 +30,22 @@ std::uint32_t UIRenderColor(const Color& color) {
}
class CGraphicTexture;
// A file-backed texture is named by its pack path; a memory texture (CGraphicImageTexture::Create,
// filled through Lock/Unlock: the CGraphicFontTexture glyph pages) by "mem:<id>@<revision>", the
// revision counting Unlocks so the canvas knows when to fetch the pixels again.
std::string UIRenderTextureName(const CGraphicTexture* texture);
// The pixels of memory texture "mem:<id>" (the part of the name before '@'), as 0xAARRGGBB.
struct UIMemoryTexture {
int width = 0;
int height = 0;
std::uint32_t revision = 0;
std::vector<std::uint32_t> argb;
};
bool UIRenderMemoryTexture(const std::string& name, UIMemoryTexture* out);
struct IDirect3DTexture8;
void UIRenderReleaseMemoryTexture(IDirect3DTexture8* texture);
// A textured quad from 40250's TPDTVertex[4] (TL, TR, BL, BR) positions and texture coordinates.
// PORT: D3D8 puts pixel centres on integers, which is why 40250 subtracts 0.5 from every vertex; the
// Godot canvas puts them on .5, so the quad is shifted back by +0.5 here.
+100 -9
View File
@@ -1,18 +1,109 @@
// 40250 EterLib/Util.cpp 的“默认代码页”那一小块。
// 40250 EterLib/Util.cpp:140-297, the default code page and font face.
//
// Util.cpp 本身是逻辑单元(LoadTextData / base64 / TokenTo*),但代码页这几个函数和它们旁边的
// GetFontFaceFromCodePage 是连在一起的,后者靠 GDI 的 EnumFontFamiliesEx 枚举字体,win32 shim 里没有,
// 真正的实现要等 2V0-f 的文字渲染。所以整份 Util.cpp 的移植留到那时,这里只放现在真正被调用到的
// GetDefaultCodePage:EterPythonLib/PythonWindow.cpp 的 CTextLine::OnChangePosition 用它判断 CP_ARABIC。
//
// 默认值和 40250 一致:gs_codePage = 0,也就是“还没有 SetDefaultCodePage”。40250 在
// PythonApplication 里根据 locale 调 SetDefaultCodePage(),那条路径同样等 2V0-d。
// The rest of Util.cpp (LoadTextData / base64 / TokenTo*) is logic and not here. The 9x face table has
// CP949/Shift-JIS/GBK/Big5 literals, so this block is copied by hand instead of port_copy.
#include "EterLib/StdAfx.h"
#include "EterLib/Util.h"
#include "EterLocale/CodePageId.h"
static DWORD gs_codePage = 0;
static std::string gs_fontFace="";
static DWORD gs_codePage=0;
int GetCharsetFromCodePage(WORD codePage)
{
switch( codePage )
{
case CP_932:
return SHIFTJIS_CHARSET;
case CP_949:
return HANGUL_CHARSET;
case CP_936:
return GB2312_CHARSET;
case CP_950:
return CHINESEBIG5_CHARSET;
case CP_1253:
return GREEK_CHARSET;
case CP_1254:
return TURKISH_CHARSET;
case CP_1255:
return HEBREW_CHARSET;
case CP_1256:
return ARABIC_CHARSET;
case CP_1257:
return BALTIC_CHARSET;
case CP_1258:
return VIETNAMESE_CHARSET;
case CP_874:
return THAI_CHARSET;
case CP_1250:
return EASTEUROPE_CHARSET;
case CP_1251:
return RUSSIAN_CHARSET;
default:
return DEFAULT_CHARSET;
}
}
const char* GetFontFaceFromCodePageNT(WORD codePage)
{
switch( codePage )
{
case CP_932:
return "MS PGothic";
case CP_949:
return "GulimChe";
case CP_936:
return "SimSun";
case CP_950:
return "MingLiU";
case CP_874:
return "Tahoma";
case CP_1252:
return "Arial";
case CP_1256:
return "Tahoma";
case CP_1258:
return "Tahoma";
case CP_65001:
return "Arial";
default:
return "Arial";
}
}
DWORD GetDefaultCodePage()
{
return gs_codePage;
}
const char * GetDefaultFontFace()
{
return gs_fontFace.c_str();
}
// PORT: 40250 asks EnumFontFamiliesEx for the 9x (native-script) face name, then the NT one. The native
// names never match a font file here; CreateFontIndirect (platform/Win32Gdi.cpp) substitutes a missing
// face, so the NT name is the answer.
const char* GetFontFaceFromCodePage(WORD codePage)
{
return GetFontFaceFromCodePageNT(codePage);
}
void SetDefaultFontFace(const char* fontFace)
{
gs_fontFace=fontFace;
}
bool SetDefaultCodePage(DWORD codePage)
{
gs_codePage=codePage;
std::string fontFace=GetFontFaceFromCodePage(codePage);
if (fontFace.empty())
return false;
SetDefaultFontFace(fontFace.c_str());
return true;
}
@@ -6,6 +6,7 @@
#include "UserInterface/StdAfx.h" // initpack and the rest of the module initializer list
#include "EterPythonLib/StdAfx.h"
#include "EterBase/Timer.h"
#include "EterLib/Util.h"
#include "PythonBoot.h"
#include "PythonHost.h"
#include "../EterLib/UIRenderCommands.h"
@@ -176,6 +177,10 @@ bool Start(const char* stdlib_path, std::string* error)
if (g_launcher)
return true;
// 40250 Main (UserInterface.cpp:410): if (LocaleService_LoadGlobal(hInstance)) SetDefaultCodePage(...).
// PORT: the platform LocaleService is already the supplied locale.cfg (en, code page 1252).
SetDefaultCodePage(LocaleService_GetCodePage());
const std::string stdlib = (stdlib_path && *stdlib_path) ? stdlib_path : PythonHost::DefaultStdLibPath();
if (stdlib.empty())
return fail(error, "no Python standard library path (Metin2Python.stdlib_path / $MT_PYTHON_STDLIB)");
@@ -2,16 +2,20 @@
#include "ScriptLib/Resource.h"
#include "EterLib/GrpImage.h"
#include "EterLib/GrpSubImage.h"
#include "EterLib/GrpText.h"
namespace {
CResource* NewUIImage(const char* name) { return new CGraphicImage(name); }
CResource* NewUISubImage(const char* name) { return new CGraphicSubImage(name); }
// 40250 UserInterface/PythonApplication.cpp NewText: a "<face>:<size>[i].fnt" name is a CGraphicText.
CResource* NewText(const char* name) { return new CGraphicText(name); }
}
CPythonResource::CPythonResource() {
for (const char* ext : {"tga", "dds", "png", "jpg", "bmp"})
m_resManager.RegisterResourceNewFunctionPointer(ext, NewUIImage);
m_resManager.RegisterResourceNewFunctionPointer("sub", NewUISubImage);
m_resManager.RegisterResourceNewFunctionPointer("fnt", NewText);
}
CPythonResource::~CPythonResource() { m_resManager.DestroyDeletingList(); }
void CPythonResource::Destroy() { m_resManager.DestroyDeletingList(); m_resManager.Destroy(); }
@@ -155,7 +155,6 @@ int g_iAccumulationTime = 0;
bool LoadLocaleData(const char*) { MT_PLATFORM_STUB(); return false; }
void SetGuildSymbolPath(const char*) { MT_PLATFORM_STUB(); }
void DefaultFont_SetName(const char*) { MT_PLATFORM_STUB(); }
void TextTail_SetLivingTime(long) { MT_PLATFORM_STUB(); }
namespace {
// The supplied 40250 Client/locale.cfg selects "en" (code page 1252).
@@ -173,3 +172,72 @@ void LocaleService_ForceSetLocale(const char* name, const char* path) {
locale_name = name ? name : "";
locale_path = path ? path : "";
}
// 40250 UserInterface/GameType.cpp:9-73 (DEFAULT_FONT), verbatim.
// PORT: the default names are CP949 literals in 40250; ui.py sets the locale font (app.SetDefaultFontName)
// before anything asks for it.
static std::string gs_strDefaultFontName = "굴림체:12.fnt";
static std::string gs_strDefaultItalicFontName = "굴림체:12i.fnt";
static CResource* gs_pkDefaultFont = NULL;
static CResource* gs_pkDefaultItalicFont = NULL;
static bool gs_isReloadDefaultFont = false;
void DefaultFont_Startup()
{
gs_pkDefaultFont = NULL;
}
void DefaultFont_Cleanup()
{
if (gs_pkDefaultFont)
gs_pkDefaultFont->Release();
}
void DefaultFont_SetName(const char * c_szFontName)
{
gs_strDefaultFontName = c_szFontName;
gs_strDefaultFontName += ".fnt";
gs_strDefaultItalicFontName = c_szFontName;
if(strchr(c_szFontName, ':'))
gs_strDefaultItalicFontName += "i";
gs_strDefaultItalicFontName += ".fnt";
gs_isReloadDefaultFont = true;
}
bool ReloadDefaultFonts()
{
CResourceManager& rkResMgr = CResourceManager::Instance();
gs_isReloadDefaultFont = false;
CResource* pkNewFont = rkResMgr.GetResourcePointer(gs_strDefaultFontName.c_str());
pkNewFont->AddReference();
if (gs_pkDefaultFont)
gs_pkDefaultFont->Release();
gs_pkDefaultFont = pkNewFont;
CResource* pkNewItalicFont = rkResMgr.GetResourcePointer(gs_strDefaultItalicFontName.c_str());
pkNewItalicFont->AddReference();
if (gs_pkDefaultItalicFont)
gs_pkDefaultItalicFont->Release();
gs_pkDefaultItalicFont = pkNewItalicFont;
return true;
}
CResource* DefaultFont_GetResource()
{
if (!gs_pkDefaultFont || gs_isReloadDefaultFont)
ReloadDefaultFonts();
return gs_pkDefaultFont;
}
CResource* DefaultItalicFont_GetResource()
{
if (!gs_pkDefaultItalicFont || gs_isReloadDefaultFont)
ReloadDefaultFonts();
return gs_pkDefaultItalicFont;
}
+457
View File
@@ -0,0 +1,457 @@
// The <wingdi.h> subset the 40250 font path calls (common/shim/win32/wingdi.h), over FreeType.
// CGraphicDib / CGraphicFontTexture run unchanged on top of it: a memory DC owns a 32-bit DIB,
// CreateFontIndirect maps the LOGFONT onto a TrueType face the way GDI sizes it, and TextOutW
// rasterises into the DIB with the DC's text/background colours.
//
// GDI sizing that the glyph atlas depends on:
// - lfHeight > 0 is the cell height (ascent + descent), lfHeight < 0 the em height; the em is
// lfHeight * unitsPerEm / (usWinAscent + usWinDescent), rounded to whole pixels.
// - tmAscent / tmDescent are usWinAscent / usWinDescent scaled to that em and rounded;
// GetTextExtentPoint32W reports tmHeight = tmAscent + tmDescent as the height.
// - ANTIALIASED_QUALITY follows the face's gasp table: grayscale only in the ppem ranges it
// flags GASP_DO_GRAY, otherwise bilevel (Tahoma at 10/12 ppem is bilevel).
// - The glyph origin is (x + A, y + tmAscent - top); advance = A + B + C.
// Missing glyphs fall back to the first installed face that has them, like GDI font linking.
#include "EterLib/StdAfx.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_TRUETYPE_TABLES_H
#include FT_GASP_H
#include FT_SYNTHESIS_H
#include FT_DRIVER_H
#include FT_MODULE_H
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>
#include <dirent.h>
namespace {
enum class GdiKind { Bitmap, Font };
struct GdiObject
{
GdiKind kind;
};
} // namespace
struct HBITMAP__ : GdiObject
{
int width = 0;
int height = 0;
bool top_down = true;
std::vector<uint32_t> bits;
};
struct HFONT__ : GdiObject
{
FT_Face face = nullptr;
std::vector<FT_Face> fallbacks;
int ppem = 0;
int ascent = 0;
int descent = 0;
bool italic = false;
bool antialiased = false;
};
struct HDC__
{
HBITMAP bitmap = nullptr;
HFONT font = nullptr;
COLORREF text = 0;
COLORREF bk = 0x00FFFFFF;
int bk_mode = OPAQUE;
};
namespace {
std::mutex g_mutex;
FT_Library g_library = nullptr;
std::unordered_map<std::string, FT_Face> g_faces; // loaded face per file path
FT_Library library()
{
if (!g_library) {
FT_Init_FreeType(&g_library);
// GDI runs the full TrueType bytecode, which is FreeType's v35 interpreter; v40 drops the
// horizontal hints and gives different advances at small sizes.
FT_UInt version = TT_INTERPRETER_VERSION_35;
FT_Property_Set(g_library, "truetype", "interpreter-version", &version);
}
return g_library;
}
std::string lower(std::string s)
{
for (char& c : s) c = char(std::tolower(static_cast<unsigned char>(c)));
return s;
}
const std::vector<std::string>& font_dirs()
{
static const std::vector<std::string> dirs = {
#if defined(__ANDROID__)
"/system/fonts",
#else
"/System/Library/Fonts/Supplemental",
"/System/Library/Fonts",
"/Library/Fonts",
#endif
};
return dirs;
}
// Face name -> font file: the file stem matched case-insensitively ("Tahoma" -> Tahoma.ttf).
std::string find_font_file(const std::string& face)
{
const std::string want = lower(face);
for (const std::string& dir : font_dirs()) {
DIR* d = opendir(dir.c_str());
if (!d) continue;
std::string hit;
while (dirent* e = readdir(d)) {
std::string name = e->d_name;
const size_t dot = name.rfind('.');
if (dot == std::string::npos) continue;
const std::string ext = lower(name.substr(dot + 1));
if (ext != "ttf" && ext != "ttc" && ext != "otf") continue;
if (lower(name.substr(0, dot)) == want) { hit = dir + "/" + name; break; }
}
closedir(d);
if (!hit.empty()) return hit;
}
return {};
}
FT_Face open_face(const std::string& path)
{
auto it = g_faces.find(path);
if (it != g_faces.end()) return it->second;
FT_Face face = nullptr;
if (FT_New_Face(library(), path.c_str(), 0, &face) != 0) face = nullptr;
if (face) FT_Select_Charmap(face, FT_ENCODING_UNICODE);
g_faces[path] = face;
return face;
}
FT_Face open_named(const char* name)
{
const std::string path = find_font_file(name);
return path.empty() ? nullptr : open_face(path);
}
// PORT: the faces GDI would substitute when the requested one is not installed (the 40250 locale
// asks for Tahoma / Arial on code page 1252), then the font-linking faces for CJK/Hangul glyphs.
const char* const kSubstitutes[] = {"Arial", "Helvetica", "Roboto-Regular", "DroidSans", "NotoSans-Regular"};
const char* const kLinked[] = {"Arial Unicode", "AppleSDGothicNeo", "NotoSansCJK-Regular", "NotoSansCJKkr-Regular",
"DroidSansFallback"};
bool gray_at(FT_Face face, int ppem)
{
const FT_Int flags = FT_Get_Gasp(face, FT_UInt(ppem));
if (flags == FT_GASP_NO_TABLE) return true;
return (flags & FT_GASP_DO_GRAY) != 0;
}
void set_size(FT_Face face, int ppem)
{
FT_Set_Pixel_Sizes(face, 0, FT_UInt(ppem));
}
struct Glyph
{
int left = 0; // A
int top = 0; // rows above the baseline
int advance = 0; // A + B + C
int width = 0; // B
int rows = 0;
int pitch = 0;
bool mono = true;
std::vector<unsigned char> bitmap;
};
bool load_glyph(HFONT font, wchar_t ch, bool render, Glyph* out)
{
FT_Face face = font->face;
FT_UInt index = FT_Get_Char_Index(face, FT_ULong(ch));
if (index == 0) {
for (FT_Face fallback : font->fallbacks) {
FT_UInt i = FT_Get_Char_Index(fallback, FT_ULong(ch));
if (i) { face = fallback; index = i; break; }
}
}
set_size(face, font->ppem);
const bool gray = font->antialiased && gray_at(face, font->ppem);
const FT_Int32 target = gray ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO;
if (FT_Load_Glyph(face, index, FT_LOAD_DEFAULT | target) != 0) return false;
FT_GlyphSlot slot = face->glyph;
if (font->italic) FT_GlyphSlot_Oblique(slot);
out->advance = int((slot->advance.x + 32) >> 6);
if (slot->format == FT_GLYPH_FORMAT_OUTLINE) {
if (FT_Render_Glyph(slot, gray ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO) != 0) return false;
}
const FT_Bitmap& bm = slot->bitmap;
out->left = slot->bitmap_left;
out->top = slot->bitmap_top;
out->width = int(bm.width);
out->rows = int(bm.rows);
out->mono = bm.pixel_mode == FT_PIXEL_MODE_MONO;
if (render && bm.buffer && bm.rows) {
out->pitch = std::abs(bm.pitch);
out->bitmap.assign(bm.buffer, bm.buffer + size_t(out->pitch) * bm.rows);
}
return true;
}
HFONT default_font()
{
static HFONT font = [] {
LOGFONT lf{};
lf.lfHeight = 12;
std::strcpy(lf.lfFaceName, "Arial");
return CreateFontIndirect(&lf);
}();
return font;
}
HFONT dc_font(HDC hdc)
{
return hdc->font ? hdc->font : default_font();
}
// COLORREF is 0x00BBGGRR; a 32-bit DIB pixel is 0x00RRGGBB.
uint32_t dib_pixel(COLORREF c)
{
return ((c & 0xFF) << 16) | (c & 0xFF00) | ((c >> 16) & 0xFF);
}
uint32_t* dib_row(HBITMAP bmp, int y)
{
const int row = bmp->top_down ? y : bmp->height - 1 - y;
return bmp->bits.data() + size_t(row) * size_t(bmp->width);
}
} // namespace
HDC CreateCompatibleDC(HDC)
{
return new HDC__();
}
BOOL DeleteDC(HDC hdc)
{
delete hdc;
return TRUE;
}
HBITMAP CreateDIBSection(HDC, const BITMAPINFO* pbmi, UINT, void** ppvBits, HANDLE, DWORD)
{
if (!pbmi || pbmi->bmiHeader.biBitCount != 32 || pbmi->bmiHeader.biCompression != BI_RGB) return nullptr;
const LONG w = pbmi->bmiHeader.biWidth;
const LONG h = pbmi->bmiHeader.biHeight;
if (w <= 0 || h == 0) return nullptr;
auto* bmp = new HBITMAP__();
bmp->kind = GdiKind::Bitmap;
bmp->width = int(w);
bmp->height = int(h < 0 ? -h : h);
bmp->top_down = h < 0;
bmp->bits.assign(size_t(bmp->width) * size_t(bmp->height), 0);
if (ppvBits) *ppvBits = bmp->bits.data();
return bmp;
}
// The only caller is CGraphicDib::Put onto a window DC, which this platform does not have.
int SetDIBitsToDevice(HDC, int, int, DWORD, DWORD h, int, int, UINT, UINT, const void*, const BITMAPINFO*, UINT)
{
return int(h);
}
HFONT CreateFontIndirect(const LOGFONT* lplf)
{
if (!lplf) return nullptr;
std::lock_guard<std::mutex> lock(g_mutex);
FT_Face face = open_named(lplf->lfFaceName);
for (const char* name : kSubstitutes) {
if (face) break;
face = open_named(name);
}
if (!face) return nullptr;
auto* font = new HFONT__();
font->kind = GdiKind::Font;
font->face = face;
for (const char* name : kLinked)
if (FT_Face linked = open_named(name); linked && linked != face) font->fallbacks.push_back(linked);
font->italic = lplf->lfItalic != 0;
font->antialiased = lplf->lfQuality == ANTIALIASED_QUALITY;
const TT_OS2* os2 = static_cast<const TT_OS2*>(FT_Get_Sfnt_Table(face, FT_SFNT_OS2));
const double upem = face->units_per_EM ? face->units_per_EM : 2048;
const double win_ascent = os2 && os2->usWinAscent ? os2->usWinAscent : face->ascender;
const double win_descent = os2 && os2->usWinAscent ? os2->usWinDescent : -face->descender;
LONG height = lplf->lfHeight ? lplf->lfHeight : 12;
int ppem = height > 0 ? int(std::lround(height * upem / (win_ascent + win_descent))) : int(-height);
font->ppem = std::max(ppem, 1);
font->ascent = int(std::lround(win_ascent * font->ppem / upem));
font->descent = int(std::lround(win_descent * font->ppem / upem));
return font;
}
BOOL DeleteObject(HGDIOBJ ho)
{
if (!ho) return FALSE;
auto* object = static_cast<GdiObject*>(ho);
if (object->kind == GdiKind::Bitmap) delete static_cast<HBITMAP__*>(object);
else delete static_cast<HFONT__*>(object); // faces stay cached for the process
return TRUE;
}
HGDIOBJ SelectObject(HDC hdc, HGDIOBJ h)
{
if (!hdc || !h) return nullptr;
auto* object = static_cast<GdiObject*>(h);
if (object->kind == GdiKind::Bitmap) {
HGDIOBJ old = hdc->bitmap;
hdc->bitmap = static_cast<HBITMAP__*>(object);
return old;
}
HGDIOBJ old = hdc->font;
hdc->font = static_cast<HFONT__*>(object);
return old;
}
COLORREF SetTextColor(HDC hdc, COLORREF color)
{
const COLORREF old = hdc->text;
hdc->text = color;
return old;
}
COLORREF SetBkColor(HDC hdc, COLORREF color)
{
const COLORREF old = hdc->bk;
hdc->bk = color;
return old;
}
int SetBkMode(HDC hdc, int mode)
{
const int old = hdc->bk_mode;
hdc->bk_mode = mode;
return old;
}
BOOL GetTextExtentPoint32W(HDC hdc, LPCWSTR lpString, int c, LPSIZE psizl)
{
if (!hdc || !psizl) return FALSE;
std::lock_guard<std::mutex> lock(g_mutex);
HFONT font = dc_font(hdc);
if (!font) return FALSE;
LONG cx = 0;
for (int i = 0; i < c; ++i) {
Glyph glyph;
if (load_glyph(font, lpString[i], false, &glyph)) cx += glyph.advance;
}
psizl->cx = cx;
psizl->cy = font->ascent + font->descent;
return TRUE;
}
BOOL GetCharABCWidthsFloatW(HDC hdc, UINT iFirst, UINT iLast, LPABCFLOAT lpABC)
{
if (!hdc || !lpABC || iLast < iFirst) return FALSE;
std::lock_guard<std::mutex> lock(g_mutex);
HFONT font = dc_font(hdc);
if (!font) return FALSE;
for (UINT ch = iFirst; ch <= iLast; ++ch) {
Glyph glyph;
if (!load_glyph(font, wchar_t(ch), false, &glyph)) return FALSE;
ABCFLOAT& abc = lpABC[ch - iFirst];
if (glyph.width == 0) {
// A blank glyph (space) is all advance, like GDI reports it.
abc.abcfA = 0;
abc.abcfB = float(glyph.advance);
abc.abcfC = 0;
} else {
abc.abcfA = float(glyph.left);
abc.abcfB = float(glyph.width);
abc.abcfC = float(glyph.advance - glyph.left - glyph.width);
}
}
return TRUE;
}
BOOL TextOutW(HDC hdc, int x, int y, LPCWSTR lpString, int c)
{
if (!hdc || !hdc->bitmap) return FALSE;
std::lock_guard<std::mutex> lock(g_mutex);
HFONT font = dc_font(hdc);
if (!font) return FALSE;
HBITMAP bmp = hdc->bitmap;
const int height = font->ascent + font->descent;
std::vector<Glyph> glyphs(size_t(std::max(c, 0)));
int extent = 0;
for (int i = 0; i < c; ++i) {
load_glyph(font, lpString[i], true, &glyphs[size_t(i)]);
extent += glyphs[size_t(i)].advance;
}
if (hdc->bk_mode == OPAQUE) {
const uint32_t bk = dib_pixel(hdc->bk);
for (int py = std::max(y, 0); py < std::min(y + height, bmp->height); ++py) {
uint32_t* row = dib_row(bmp, py);
for (int px = std::max(x, 0); px < std::min(x + extent, bmp->width); ++px) row[px] = bk;
}
}
const uint32_t fg = dib_pixel(hdc->text);
const uint32_t bg = dib_pixel(hdc->bk);
int pen = x;
for (const Glyph& glyph : glyphs) {
const int ox = pen + glyph.left;
const int oy = y + font->ascent - glyph.top;
for (int gy = 0; gy < glyph.rows && !glyph.bitmap.empty(); ++gy) {
const int py = oy + gy;
if (py < 0 || py >= bmp->height) continue;
uint32_t* row = dib_row(bmp, py);
const unsigned char* src = glyph.bitmap.data() + size_t(gy) * size_t(glyph.pitch);
for (int gx = 0; gx < glyph.width; ++gx) {
const int px = ox + gx;
if (px < 0 || px >= bmp->width) continue;
unsigned coverage = glyph.mono ? ((src[gx >> 3] >> (7 - (gx & 7))) & 1) * 255 : src[gx];
if (!coverage) continue;
if (coverage == 255) { row[px] = fg; continue; }
// Grayscale GDI text blends the text colour over the background colour.
const uint32_t under = hdc->bk_mode == OPAQUE ? bg : row[px];
uint32_t out = 0;
for (int shift = 0; shift < 24; shift += 8) {
const unsigned a = (fg >> shift) & 0xFF, b = (under >> shift) & 0xFF;
out |= ((a * coverage + b * (255 - coverage) + 127) / 255) << shift;
}
row[px] = out;
}
}
pen += glyph.advance;
}
return TRUE;
}
BOOL TextOutA(HDC hdc, int x, int y, LPCSTR lpString, int c)
{
const int n = MultiByteToWideChar(CP_ACP, 0, lpString, c, nullptr, 0);
if (n <= 0) return c == 0;
std::vector<WCHAR> wide(static_cast<size_t>(n));
MultiByteToWideChar(CP_ACP, 0, lpString, c, wide.data(), n);
return TextOutW(hdc, x, y, wide.data(), n);
}
+10
View File
@@ -31,6 +31,10 @@ target_include_directories(port_logic PUBLIC ${MT_PORT_SHIMS})
# Libraries 40250 links that are vendored as-is: <lzo/lzo1x.h> (EterBase/lzo.h), <cryptopp/*>
# (EterBase/cipher.h), <Python-2.7/*> (ScriptLib).
target_link_libraries(port_logic PUBLIC mt3p::minilzo mt3p::cryptopp)
# common/Win32Crt.cpp converts non-UTF-8 code pages with iconv (MT_ICONV_LIBRARY, extension/CMakeLists.txt).
if(MT_ICONV_LIBRARY)
target_link_libraries(port_logic PUBLIC ${MT_ICONV_LIBRARY})
endif()
if(TARGET mtpython)
target_link_libraries(port_logic PUBLIC mt3p::python)
endif()
@@ -164,6 +168,12 @@ if(BUILD_TESTING AND CMAKE_SYSTEM_NAME STREQUAL CMAKE_HOST_SYSTEM_NAME)
add_test(NAME port.eterpack COMMAND $<TARGET_FILE:port_eterpack_test> ${MT_40250_CLIENT})
set_tests_properties(port.eterpack PROPERTIES SKIP_RETURN_CODE 77)
# 批次 2V0-g: the 40250 font path over the FreeType GDI emulation; needs a system Tahoma.
add_executable(port_text_test ${CMAKE_CURRENT_SOURCE_DIR}/../../tests/port_text_test.cpp)
target_link_libraries(port_text_test PRIVATE port_platform)
add_test(NAME port.text COMMAND $<TARGET_FILE:port_text_test>)
set_tests_properties(port.text PROPERTIES SKIP_RETURN_CODE 77)
if(TARGET mtpython)
add_executable(port_python_launcher_test ${CMAKE_CURRENT_SOURCE_DIR}/../../tests/port_python_launcher_test.cpp)
target_link_libraries(port_python_launcher_test PRIVATE port_platform)
+209
View File
@@ -0,0 +1,209 @@
#include "StdAfx.h"
#include "TextTag.h"
int GetTextTag(const wchar_t * src, int maxLen, int & tagLen, std::wstring & extraInfo)
{
tagLen = 1;
if (maxLen < 2 || *src != L'|')
return TEXT_TAG_PLAIN;
const wchar_t * cur = ++src;
if (*cur == L'c') // color
{
if (maxLen < 10)
return TEXT_TAG_PLAIN;
tagLen = 10;
extraInfo.assign(++cur, 8);
return TEXT_TAG_COLOR;
}
else if (*cur == L'|') // ||는 |로 표시한다.
{
tagLen = 2;
return TEXT_TAG_TAG;
}
else if (*cur == L'r') // restore color
{
tagLen = 2;
return TEXT_TAG_RESTORE_COLOR;
}
else if (*cur == L'H') // hyperlink |Hitem:10000:0:0:0:0|h[이름]|h
{
tagLen = 2;
return TEXT_TAG_HYPERLINK_START;
}
else if (*cur == L'h') // end of hyperlink
{
tagLen = 2;
return TEXT_TAG_HYPERLINK_END;
}
return TEXT_TAG_PLAIN;
}
std::wstring GetTextTagOutputString(const wchar_t * src, int src_len)
{
int len;
std::wstring dst;
std::wstring extraInfo;
int output_len = 0;
int hyperlinkStep = 0;
for (int i = 0; i < src_len; )
{
int tag = GetTextTag(&src[i], src_len - i, len, extraInfo);
if (tag == TEXT_TAG_PLAIN || tag == TEXT_TAG_TAG)
{
if (hyperlinkStep == 0)
{
++output_len;
dst += src[i];
}
}
else if (tag == TEXT_TAG_HYPERLINK_START)
hyperlinkStep = 1;
else if (tag == TEXT_TAG_HYPERLINK_END)
hyperlinkStep = 0;
i += len;
}
return dst;
}
int GetTextTagInternalPosFromRenderPos(const wchar_t * src, int src_len, int offset)
{
int len;
std::wstring dst;
std::wstring extraInfo;
int output_len = 0;
int hyperlinkStep = 0;
bool color_tag = false;
int internal_offset = 0;
for (int i = 0; i < src_len; )
{
int tag = GetTextTag(&src[i], src_len - i, len, extraInfo);
if (tag == TEXT_TAG_COLOR)
{
color_tag = true;
internal_offset = i;
}
else if (tag == TEXT_TAG_RESTORE_COLOR)
{
color_tag = false;
}
else if (tag == TEXT_TAG_PLAIN || tag == TEXT_TAG_TAG)
{
if (hyperlinkStep == 0)
{
if (!color_tag)
internal_offset = i;
if (offset <= output_len)
return internal_offset;
++output_len;
dst += src[i];
}
}
else if (tag == TEXT_TAG_HYPERLINK_START)
hyperlinkStep = 1;
else if (tag == TEXT_TAG_HYPERLINK_END)
hyperlinkStep = 0;
i += len;
}
return internal_offset;
}
int GetTextTagOutputLen(const wchar_t * src, int src_len)
{
int len;
std::wstring extraInfo;
int output_len = 0;
int hyperlinkStep = 0;
for (int i = 0; i < src_len; )
{
int tag = GetTextTag(&src[i], src_len - i, len, extraInfo);
if (tag == TEXT_TAG_PLAIN || tag == TEXT_TAG_TAG)
{
if (hyperlinkStep == 0)
++output_len;
}
else if (tag == TEXT_TAG_HYPERLINK_START)
hyperlinkStep = 1;
else if (tag == TEXT_TAG_HYPERLINK_END)
hyperlinkStep = 0;
i += len;
}
return output_len;
}
int FindColorTagStartPosition(const wchar_t * src, int src_len)
{
if (src_len < 2)
return 0;
const wchar_t * cur = src;
// |r의 경우
if (*cur == L'r' && *(cur - 1) == L'|')
{
int len = src_len;
// ||r은 무시
if (len >= 2 && *(cur - 2) == L'|')
return 1;
cur -= 2;
len -= 2;
// |c까지 찾아서 |위치까지 리턴한다.
while (len > 1) // 최소 2자를 검사해야 된다.
{
if (*cur == L'c' && *(cur - 1) == L'|')
return (src - cur) + 1;
--cur;
--len;
}
return (src_len); // 못찾으면 전부;;
}
// ||의 경우
else if (*cur == L'|' && *(cur - 1) == L'|')
return 1;
return 0;
}
int FindColorTagEndPosition(const wchar_t * src, int src_len)
{
const wchar_t * cur = src;
if (src_len >= 4 && *cur == L'|' && *(cur + 1) == L'c')
{
int left = src_len - 2;
cur += 2;
while (left > 1)
{
if (*cur == L'|' && *(cur + 1) == L'r')
return (cur - src) + 1;
--left;
++cur;
}
}
else if (src_len >= 2 && *cur == L'|' && *(cur + 1) == L'|')
return 1;
return 0;
}
+17
View File
@@ -0,0 +1,17 @@
#pragma once // PORT: 40250 TextTag.h has no include guard; the header gate includes every header twice.
enum
{
TEXT_TAG_PLAIN,
TEXT_TAG_TAG, // ||
TEXT_TAG_COLOR, // |cffffffff
TEXT_TAG_HYPERLINK_START, // |H
TEXT_TAG_HYPERLINK_END, // |h ex) |Hitem:1234:1:1:1|h
TEXT_TAG_RESTORE_COLOR,
};
extern int GetTextTag(const wchar_t * src, int maxLen, int & tagLen, std::wstring & extraInfo);
extern std::wstring GetTextTagOutputString(const wchar_t * src, int src_len);
extern int GetTextTagOutputLen(const wchar_t * src, int len);
extern int FindColorTagEndPosition(const wchar_t * src, int src_len);
extern int FindColorTagStartPosition(const wchar_t * src, int src_len);
extern int GetTextTagInternalPosFromRenderPos(const wchar_t * src, int src_len, int offset);
+366
View File
@@ -0,0 +1,366 @@
#include "StdAfx.h"
#include "Arabic.h"
#include <assert.h>
enum ARABIC_CODE
{
ARABIC_CODE_BASE = 0x0621,
ARABIC_CODE_LAST = 0x064a,
ARABIC_CODE_COUNT = ARABIC_CODE_LAST - ARABIC_CODE_BASE + 1,
};
enum ARABIC_FORM_TYPE
{
DEBUG_CODE,
ISOLATED,
INITIAL,
MEDIAL,
FINAL,
ARABIC_FORM_TYPE_NUM,
};
bool Arabic_IsInSpace(wchar_t code)
{
switch (code)
{
case ' ':
case '\t':
return true;
default:
return false;
}
}
bool Arabic_IsInSymbol(wchar_t code)
{
return (code >= 0x20 && code <= 0x2f) ||
(code >= 0x3a && code <= 0x40) ||
(code >= 0x5b && code <= 0x60) ||
(code >= 0x7b && code <= 0x7e);
}
bool Arabic_IsInPresentation(wchar_t code)
{
return (code >= 0xfb50 && code <= 0xfdff) || (code >= 0xfe70 && code <= 0xfeff) || (code == 0x08);
}
bool Arabic_HasPresentation(wchar_t* codes, int last)
{
while (last > 0)
{
if (Arabic_IsInSpace(codes[last]))
{
last--;
}
else
{
if (Arabic_IsInPresentation(codes[last]))
return true;
else
break;
}
}
return false;
}
wchar_t Arabic_GetComposition(wchar_t cur, wchar_t next, ARABIC_FORM_TYPE pos)
{
static wchar_t LAM_ALEF_MADDA[ARABIC_FORM_TYPE_NUM] = {0x0622, 0xFEF5, 0, 0, 0xFEF6};
static wchar_t LAM_ALEF_HAMZA_ABOVE[ARABIC_FORM_TYPE_NUM] = {0x0623, 0xFEF7, 0, 0, 0xFEF8};
static wchar_t LAM_ALEF_HAMZA_BELOW[ARABIC_FORM_TYPE_NUM] = {0x0625, 0xFEF9, 0, 0, 0xFEFA};
static wchar_t LAM_ALEF[ARABIC_FORM_TYPE_NUM] = {0x0627, 0xFEFB, 0, 0, 0xFEFC};
switch (cur)
{
case 0x0644:
switch (next)
{
case 0x0622:return LAM_ALEF_MADDA[pos];break;
case 0x0623:return LAM_ALEF_HAMZA_ABOVE[pos];break;
case 0x0625:return LAM_ALEF_HAMZA_BELOW[pos];break;
case 0x0627:return LAM_ALEF[pos];break;
}
break;
}
return 0;
}
wchar_t Arabic_GetMap(wchar_t code, ARABIC_FORM_TYPE pos)
{
static wchar_t HAMZA[ARABIC_FORM_TYPE_NUM] = {0x0621 , 0xFE80, 0, 0, 0};
static wchar_t ALEF_MADDA[ARABIC_FORM_TYPE_NUM] = {0x0622 , 0xFE81, 0, 0, 0xFE82};
static wchar_t ALEF_HAMZA_ABOVE[ARABIC_FORM_TYPE_NUM] = {0x0623 , 0xFE83, 0, 0, 0xFE84};
static wchar_t WAW_HAMZA[ARABIC_FORM_TYPE_NUM] = {0x0624 , 0xFE85, 0, 0, 0xFE86};
static wchar_t ALEF_HAMZA_BELOW[ARABIC_FORM_TYPE_NUM] = {0x0625 , 0xFE87, 0, 0, 0xFE88};
static wchar_t YEH_HAMZA[ARABIC_FORM_TYPE_NUM] = {0x0626 , 0xFE89, 0xFE8B, 0xFE8C, 0xFE8A};
static wchar_t ALEF[ARABIC_FORM_TYPE_NUM] = {0x0627 , 0xFE8D, 0, 0, 0xFE8E};
static wchar_t BEH[ARABIC_FORM_TYPE_NUM] = {0x0628 , 0xFE8F, 0xFE91, 0xFE92, 0xFE90};
static wchar_t TEH_MARBUTA[ARABIC_FORM_TYPE_NUM] = {0x0629 , 0xFE93, 0, 0, 0xFE94};
static wchar_t TEH[ARABIC_FORM_TYPE_NUM] = {0x062A , 0xFE95, 0xFE97, 0xFE98, 0xFE96};
static wchar_t THEH[ARABIC_FORM_TYPE_NUM] = {0x062B , 0xFE99, 0xFE9B, 0xFE9C, 0xFE9A};
static wchar_t JEEM[ARABIC_FORM_TYPE_NUM] = {0x062C , 0xFE9D, 0xFE9F, 0xFEA0, 0xFE9E};
static wchar_t HAH[ARABIC_FORM_TYPE_NUM] = {0x062D , 0xFEA1, 0xFEA3, 0xFEA4, 0xFEA2};
static wchar_t KHAH[ARABIC_FORM_TYPE_NUM] = {0x062E , 0xFEA5, 0xFEA7, 0xFEA8, 0xFEA6};
static wchar_t DAL[ARABIC_FORM_TYPE_NUM] = {0x062F , 0xFEA9, 0, 0, 0xFEAA};
static wchar_t THAL[ARABIC_FORM_TYPE_NUM] = {0x0630 , 0xFEAB, 0, 0, 0xFEAC};
static wchar_t REH[ARABIC_FORM_TYPE_NUM] = {0x0631 , 0xFEAD, 0, 0, 0xFEAE};
static wchar_t ZAIN[ARABIC_FORM_TYPE_NUM] = {0x0632 , 0xFEAF, 0, 0, 0xFEB0};
static wchar_t SEEN[ARABIC_FORM_TYPE_NUM] = {0x0633 , 0xFEB1, 0xFEB3, 0xFEB4, 0xFEB2};
static wchar_t SHEEN[ARABIC_FORM_TYPE_NUM] = {0x0634 , 0xFEB5, 0xFEB7, 0xFEB8, 0xFEB6};
static wchar_t SAD[ARABIC_FORM_TYPE_NUM] = {0x0635 , 0xFEB9, 0xFEBB, 0xFEBC, 0xFEBA};
static wchar_t DAD[ARABIC_FORM_TYPE_NUM] = {0x0636 , 0xFEBD, 0xFEBF, 0xFEC0, 0xFEBE};
static wchar_t TAH[ARABIC_FORM_TYPE_NUM] = {0x0637 , 0xFEC1, 0xFEC3, 0xFEC4, 0xFEC2};
static wchar_t ZAH[ARABIC_FORM_TYPE_NUM] = {0x0638 , 0xFEC5, 0xFEC7, 0xFEC8, 0xFEC6};
static wchar_t AIN[ARABIC_FORM_TYPE_NUM] = {0x0639 , 0xFEC9, 0xFECB, 0xFECC, 0xFECA};
static wchar_t GHAIN[ARABIC_FORM_TYPE_NUM] = {0x063A , 0xFECD, 0xFECF, 0xFED0, 0xFECE};
static wchar_t TATWEEL[ARABIC_FORM_TYPE_NUM] = {0x0640 , 0x0640, 0, 0, 0};
static wchar_t OX_FEH[ARABIC_FORM_TYPE_NUM] = {0x0641 , 0xFED1, 0xFED3, 0xFED4, 0xFED2};
static wchar_t QAF[ARABIC_FORM_TYPE_NUM] = {0x0642 , 0xFED5, 0xFED7, 0xFED8, 0xFED6};
static wchar_t KAF[ARABIC_FORM_TYPE_NUM] = {0x0643 , 0xFED9, 0xFEDB, 0xFEDC, 0xFEDA};
static wchar_t LAM[ARABIC_FORM_TYPE_NUM] = {0x0644 , 0xFEDD, 0xFEDF, 0xFEE0, 0xFEDE};
static wchar_t MEEM[ARABIC_FORM_TYPE_NUM] = {0x0645 , 0xFEE1, 0xFEE3, 0xFEE4, 0xFEE2};
static wchar_t NOON[ARABIC_FORM_TYPE_NUM] = {0x0646 , 0xFEE5, 0xFEE7, 0xFEE8, 0xFEE6};
static wchar_t HEH[ARABIC_FORM_TYPE_NUM] = {0x0647 , 0xFEE9, 0xFEEB, 0xFEEC, 0xFEEA};
static wchar_t WAW[ARABIC_FORM_TYPE_NUM] = {0x0648 , 0xFEED, 0, 0, 0xFEEE};
static wchar_t ALEF_MAKSURA[ARABIC_FORM_TYPE_NUM] = {0x0649 , 0xFEEF, 0, 0, 0xFEF0};
static wchar_t YEH[ARABIC_FORM_TYPE_NUM] = {0x064A , 0xFEF1, 0xFEF3, 0xFEF4, 0xFEF2};
switch (code)
{
case 0x0621: return HAMZA[pos];break;
case 0x0622: return ALEF_MADDA[pos];break;
case 0x0623: return ALEF_HAMZA_ABOVE[pos];break;
case 0x0624: return WAW_HAMZA[pos];break;
case 0x0625: return ALEF_HAMZA_BELOW[pos];break;
case 0x0626: return YEH_HAMZA[pos];break;
case 0x0627: return ALEF[pos];break;
case 0x0628: return BEH[pos];break;
case 0x0629: return TEH_MARBUTA[pos];break;
case 0x062A: return TEH[pos];break;
case 0x062B: return THEH[pos];break;
case 0x062C: return JEEM[pos];break;
case 0x062D: return HAH[pos];break;
case 0x062E: return KHAH[pos];break;
case 0x062F: return DAL[pos];break;
case 0x0630: return THAL[pos];break;
case 0x0631: return REH[pos];break;
case 0x0632: return ZAIN[pos];break;
case 0x0633: return SEEN[pos];break;
case 0x0634: return SHEEN[pos];break;
case 0x0635: return SAD[pos];break;
case 0x0636: return DAD[pos];break;
case 0x0637: return TAH[pos];break;
case 0x0638: return ZAH[pos];break;
case 0x0639: return AIN[pos];break;
case 0x063A: return GHAIN[pos];break;
case 0x0640: return TATWEEL[pos];break;
case 0x0641: return OX_FEH[pos];break;
case 0x0642: return QAF[pos];break;
case 0x0643: return KAF[pos];break;
case 0x0644: return LAM[pos];break;
case 0x0645: return MEEM[pos];break;
case 0x0646: return NOON[pos];break;
case 0x0647: return HEH[pos];break;
case 0x0648: return WAW[pos];break;
case 0x0649: return ALEF_MAKSURA[pos];break;
case 0x064A: return YEH[pos];break;
}
return 0;
}
bool Arabic_IsInMap(wchar_t code)
{
return (code >= ARABIC_CODE_BASE && code <= ARABIC_CODE_LAST);
}
bool Arabic_IsInComposing(wchar_t code)
{
switch (code)
{
case 0x064B: // FATHATAN
case 0x064C: // DAMMATAN
case 0x064D: // KASRATAN
case 0x064E: // FATHA
case 0x064F: // DAMMA
case 0x0650: // KASRA
case 0x0651: // SHADDA
case 0x0652: // SUKUN
case 0x0653: // MADDAH ABOVE
case 0x0654: // HAMZA ABOVE
case 0x0655: // HAMZA BELOW
case 0x0670: // SUPERSCRIPT ALEF
case 0x06D6: // HIGH LIG. SAD WITH LAM WITH ALEF MAKSURA
case 0x06D7: // HIGH LIG. QAF WITH LAM WITH ALEF MAKSURA
case 0x06D8: // HIGH MEEM INITIAL FORM
case 0x06D9: // HIGH LAM ALEF
case 0x06DA: // HIGH JEEM
case 0x06DB: // HIGH THREE DOTS
case 0x06DC: // HIGH SEEN
// The 2 entires below should not be here - contact unicode.org !!
// case 0x06DD: // END OF AYAH
// case 0x06DE: // START OF RUB EL HIZB
case 0x06DF: // HIGH ROUNDED ZERO
case 0x06E0: // HIGH UPRIGHT RECTANGULAR ZERO
case 0x06E1: // HIGH DOTLESS HEAD OF KHAH
case 0x06E2: // HIGH MEEM ISOLATED FORM
case 0x06E3: // LOW SEEN
case 0x06E4: // HIGH MADDA
case 0x06E7: // HIGH YEH
case 0x06E8: // HIGH NOON
case 0x06EA: // EMPTY CENTRE LOW STOP
case 0x06EB: // EMPTY CENTRE HIGH STOP
case 0x06EC: // HIGH STOP WITH FILLED CENTRE
case 0x06ED: // LOW MEEM
return true;
}
return false;
}
bool Arabic_IsNext(wchar_t code)
{
return (code == 0x0640);
}
bool Arabic_IsComb1(wchar_t code)
{
return (code == 0x0644);
}
bool Arabic_IsComb2(wchar_t code)
{
switch (code)
{
case 0x0622:
case 0x0623:
case 0x0625:
case 0x0627:
return true;
}
return false;
}
size_t Arabic_MakeShape(wchar_t* src, size_t srcLen, wchar_t* dst, size_t dstLen)
{
assert(dstLen >= srcLen);
const size_t srcLastIndex = srcLen - 1;
unsigned dstIndex = 0;
for (size_t srcIndex = 0; srcIndex < srcLen; ++srcIndex)
{
wchar_t cur = src[srcIndex];
//printf("now %x\n", cur);
if (Arabic_IsInMap(cur))
{
// 이전 글자 얻어내기
wchar_t prev = 0;
{
size_t prevIndex = srcIndex;
while (prevIndex > 0)
{
prevIndex--;
prev = src[prevIndex];
//printf("\tprev %d:%x\n", prevIndex, cur);
if (Arabic_IsInComposing(prev))
continue;
else
break;
}
if ((srcIndex == 0) ||
(!Arabic_IsInMap(prev)) ||
(!Arabic_GetMap(prev, INITIAL) && !Arabic_GetMap(prev, MEDIAL)))
{
//printf("\tprev not defined\n");
prev = 0;
}
}
// 다음 글자 얻어내기
wchar_t next = 0;
{
size_t nextIndex = srcIndex;
while (nextIndex < srcLastIndex)
{
nextIndex++;
next = src[nextIndex];
if (Arabic_IsInComposing(next))
continue;
else
break;
}
if ((nextIndex == srcLen) ||
(!Arabic_IsInMap(next)) ||
(!Arabic_GetMap(next, MEDIAL) && !Arabic_GetMap(next, FINAL) && !Arabic_IsNext(next)))
{
//printf("\tnext not defined\n");
next = 0;
}
}
if (Arabic_IsComb1(cur) && Arabic_IsComb2(next))
{
if (prev)
dst[dstIndex] = Arabic_GetComposition(cur, next, FINAL);
else
dst[dstIndex] = Arabic_GetComposition(cur, next, ISOLATED);
//printf("\tGot me a complex:%x\n", dst[dstIndex]);
srcIndex++;
dstIndex++;
}
else if (prev && next && (dst[dstIndex] = Arabic_GetMap(cur, MEDIAL)))
{
//printf("\tGot prev & next:%x\n", dst[dstIndex]);
dstIndex++;
}
else if (prev && (dst[dstIndex] = Arabic_GetMap(cur, FINAL)))
{
//printf("\tGot prev:%x\n", dst[dstIndex]);
dstIndex++;
}
else if (next && (dst[dstIndex] = Arabic_GetMap(cur, INITIAL)))
{
//printf("\tGot next:%x\n", dst[dstIndex]);
dstIndex++;
}
else
{
dst[dstIndex] = Arabic_GetMap(cur, ISOLATED);
//printf("\tGot nothing:%x\n", dst[dstIndex]);
dstIndex++;
}
}
else
{
dst[dstIndex] = cur;
dstIndex++;
}
}
return dstIndex;
}
wchar_t Arabic_ConvSymbol(wchar_t c)
{
switch (c)
{
case '(': return')';break;
case ')': return'(';break;
case '<': return'>';break;
case '>': return'<';break;
case '{': return'}';break;
case '}': return'{';break;
case '[': return']';break;
case ']': return '[';break;
default:
return c;
}
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
wchar_t Arabic_ConvSymbol(wchar_t c);
bool Arabic_IsInSpace(wchar_t code);
bool Arabic_IsInSymbol(wchar_t code);
bool Arabic_IsInPresentation(wchar_t code);
bool Arabic_HasPresentation(wchar_t* codes, int last);
size_t Arabic_MakeShape(wchar_t* src, size_t srcLen, wchar_t* dst, size_t dstLen);
wchar_t Arabic_ConvEnglishModeSymbol(wchar_t code);
@@ -0,0 +1,43 @@
#include "StdAfx.h"
#include "StringCodec.h"
#include "StringCodec_Vietnamese.h"
int Ymir_WideCharToMultiByte(
UINT CodePage,
DWORD dwFlags,
LPCWSTR lpWideCharStr,
int cchWideChar,
LPSTR lpMultiByteStr,
int cbMultiByte,
LPCSTR lpDefaultChar,
LPBOOL lpUsedDefaultChar
)
{
if (CodePage == CP_1258)
{
return EL_String_Encode_Vietnamese(lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte);
}
else
{
return WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar);
}
}
int Ymir_MultiByteToWideChar(
UINT CodePage,
DWORD dwFlags,
LPCSTR lpMultiByteStr,
int cbMultiByte,
LPWSTR lpWideCharStr,
int cchWideChar
)
{
if (CodePage == CP_1258)
{
return EL_String_Decode_Vietnamese(lpMultiByteStr, cbMultiByte, lpWideCharStr, cchWideChar);
}
else
{
return MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, lpWideCharStr, cchWideChar);
}
}
@@ -0,0 +1,21 @@
#pragma once
int Ymir_WideCharToMultiByte(
UINT CodePage,
DWORD dwFlags,
LPCWSTR lpWideCharStr,
int cchWideChar,
LPSTR lpMultiByteStr,
int cbMultiByte,
LPCSTR lpDefaultChar,
LPBOOL lpUsedDefaultChar
);
int Ymir_MultiByteToWideChar(
UINT CodePage,
DWORD dwFlags,
LPCSTR lpMultiByteStr,
int cbMultiByte,
LPWSTR lpWideCharStr,
int cchWideChar
);
@@ -0,0 +1,554 @@
#include "StdAfx.h"
#include "StringCodec_Vietnamese.h"
#pragma warning(disable: 4310) // char 짤림 경고 무시
static wchar_t cp1258_to_unicode[256] = {
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, //0x80
0x02c6, 0x2030, 0x008a, 0x2039, 0x0152, 0x008d, 0x008e, 0x008f, //
0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, //0x90
0x02dc, 0x2122, 0x009a, 0x203a, 0x0153, 0x009d, 0x009e, 0x0178, //
0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, //0xa0
0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, //
0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, //0xb0
0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, //
0x00c0, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x00c5, 0x00c6, 0x00c7, //0xc0
0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x0300, 0x00cd, 0x00ce, 0x00cf, //
0x0110, 0x00d1, 0x0309, 0x00d3, 0x00d4, 0x01a0, 0x00d6, 0x00d7, //0xd0
0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x01af, 0x0303, 0x00df, //
0x00e0, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x00e5, 0x00e6, 0x00e7, //0xe0
0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x0301, 0x00ed, 0x00ee, 0x00ef, //
0x0111, 0x00f1, 0x0323, 0x00f3, 0x00f4, 0x01a1, 0x00f6, 0x00f7, //0xf0
0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x01b0, 0x20ab, 0x00ff, //
};
static wchar_t cp1258_composed_table[][5] = {
{ 0x00c1, 0x00c0, 0x1ea2, 0x00c3, 0x1ea0 },
{ 0x00e1, 0x00e0, 0x1ea3, 0x00e3, 0x1ea1 },
{ 0x1eae, 0x1eb0, 0x1eb2, 0x1eb4, 0x1eb6 },
{ 0x1eaf, 0x1eb1, 0x1eb3, 0x1eb5, 0x1eb7 },
{ 0x1ea4, 0x1ea6, 0x1ea8, 0x1eaa, 0x1eac },
{ 0x1ea5, 0x1ea7, 0x1ea9, 0x1eab, 0x1ead },
{ 0x00c9, 0x00c8, 0x1eba, 0x1ebc, 0x1eb8 },
{ 0x00e9, 0x00e8, 0x1ebb, 0x1ebd, 0x1eb9 },
{ 0x1ebe, 0x1ec0, 0x1ec2, 0x1ec4, 0x1ec6 },
{ 0x1ebf, 0x1ec1, 0x1ec3, 0x1ec5, 0x1ec7 },
{ 0x00cd, 0x00cc, 0x1ec8, 0x0128, 0x1eca },
{ 0x00ed, 0x00ec, 0x1ec9, 0x0129, 0x1ecb },
{ 0x00d3, 0x00d2, 0x1ece, 0x00d5, 0x1ecc },
{ 0x00f3, 0x00f2, 0x1ecf, 0x00f5, 0x1ecd },
{ 0x1ed0, 0x1ed2, 0x1ed4, 0x1ed6, 0x1ed8 },
{ 0x1ed1, 0x1ed3, 0x1ed5, 0x1ed7, 0x1ed9 },
{ 0x1eda, 0x1edc, 0x1ede, 0x1ee0, 0x1ee2 },
{ 0x1edb, 0x1edd, 0x1edf, 0x1ee1, 0x1ee3 },
{ 0x00da, 0x00d9, 0x1ee6, 0x0168, 0x1ee4 },
{ 0x00fa, 0x00f9, 0x1ee7, 0x0169, 0x1ee5 },
{ 0x1ee8, 0x1eea, 0x1eec, 0x1eee, 0x1ef0 },
{ 0x1ee9, 0x1eeb, 0x1eed, 0x1eef, 0x1ef1 },
{ 0x00dd, 0x1ef2, 0x1ef6, 0x1ef8, 0x1ef4 },
{ 0x00fd, 0x1ef3, 0x1ef7, 0x1ef9, 0x1ef5 },
};
static bool IsTone(wchar_t tone)
{
switch(tone)
{
case 0x0300:
case 0x0301:
case 0x0309:
case 0x0303:
case 0x0323:
return true;
default:
return false;
}
}
static wchar_t ComposeTone(wchar_t prev, wchar_t tone)
{
int col, row;
switch(tone)
{
case 0x0301: col = 0; break;
case 0x0300: col = 1; break;
case 0x0309: col = 2; break;
case 0x0303: col = 3; break;
case 0x0323: col = 4; break;
default:
return prev;
}
switch(prev)
{
case 0x0041: row = 0; break;
case 0x0061: row = 1; break;
case 0x0102: row = 2; break;
case 0x0103: row = 3; break;
case 0x00C2: row = 4; break;
case 0x00E2: row = 5; break;
case 0x0045: row = 6; break;
case 0x0065: row = 7; break;
case 0x00CA: row = 8; break;
case 0x00EA: row = 9; break;
case 0x0049: row = 10; break;
case 0x0069: row = 11; break;
case 0x004F: row = 12; break;
case 0x006F: row = 13; break;
case 0x00D4: row = 14; break;
case 0x00F4: row = 15; break;
case 0x01A0: row = 16; break;
case 0x01A1: row = 17; break;
case 0x0055: row = 18; break;
case 0x0075: row = 19; break;
case 0x01AF: row = 20; break;
case 0x01B0: row = 21; break;
case 0x0059: row = 22; break;
case 0x0079: row = 23; break;
default:
return prev;
}
return cp1258_composed_table[row][col];
}
int EL_String_Decode_Vietnamese(const char* multi, int multiLen, wchar_t* wide, int wideLen)
{
if(multiLen < 0)
multiLen = (int)strlen(multi) + 1;
int src = 0;
int dest = 0;
if(multiLen > 0)
{
/* 첫글자는 무조건 변경 */
wchar_t prev = cp1258_to_unicode[(BYTE)multi[src++]];
while(src < multiLen)
{
wchar_t unicode = cp1258_to_unicode[(BYTE)multi[src]];
/* 다음 문자가 Tone 인가? */
if(IsTone(unicode))
{
/* 앞의 문자와 합하자. */
prev = ComposeTone(prev, unicode);
}
else
{
/* 일반 문자가 왔다. 앞 문자를 변환 */
if(dest < wideLen)
wide[dest++] = prev;
prev = unicode;
}
++src;
}
if(dest < wideLen)
wide[dest++] = prev;
}
return dest;
}
static bool DecomposeLetter(wchar_t input, char* letter)
{
switch(input)
{
case 0x00c1: // L'Á'
case 0x00c0: // L'À'
case 0x1ea2: // L'Ả'
case 0x00c3: // L'Ã'
case 0x1ea0: // L'Ạ'
*letter = 'A';
return true;
case 0x00e1: // L'á'
case 0x00e0: // L'à'
case 0x1ea3: // L'ả'
case 0x00e3: // L'ã'
case 0x1ea1: // L'ạ'
*letter = 'a';
return true;
case 0x1eae: // L'Ắ'
case 0x1eb0: // L'Ằ'
case 0x1eb2: // L'Ẳ'
case 0x1eb4: // L'Ẵ'
case 0x1eb6: // L'Ặ'
*letter = (char)0xc3;
return true;
case 0x1eaf: // L'ắ'
case 0x1eb1: // L'ằ'
case 0x1eb3: // L'ẳ'
case 0x1eb5: // L'ẵ'
case 0x1eb7: // L'ặ'
*letter = (char)0xe3;
return true;
case 0x1ea4: // L'Ấ'
case 0x1ea6: // L'Ầ'
case 0x1ea8: // L'Ẩ'
case 0x1eaa: // L'Ẫ'
case 0x1eac: // L'Ậ'
*letter = (char)0xc2;
return true;
case 0x1ea5: // L'ấ'
case 0x1ea7: // L'ầ'
case 0x1ea9: // L'ẩ'
case 0x1eab: // L'ẫ'
case 0x1ead: // L'ậ'
*letter = (char)0xe2;
return true;
case 0x00c9: // L'É'
case 0x00c8: // L'È'
case 0x1eba: // L'Ẻ'
case 0x1ebc: // L'Ẽ'
case 0x1eb8: // L'Ẹ'
*letter = (char)'E';
return true;
case 0x00e9: // L'é'
case 0x00e8: // L'è'
case 0x1ebb: // L'ẻ'
case 0x1ebd: // L'ẽ'
case 0x1eb9: // L'ẹ'
*letter = (char)'e';
return true;
case 0x1ebe: // L'Ế'
case 0x1ec0: // L'Ề'
case 0x1ec2: // L'Ể'
case 0x1ec4: // L'Ễ'
case 0x1ec6: // L'Ệ'
*letter = (char)0xca;
return true;
case 0x1ebf: // L'ế'
case 0x1ec1: // L'ề'
case 0x1ec3: // L'ể'
case 0x1ec5: // L'ễ'
case 0x1ec7: // L'ệ'
*letter = (char)0xea;
return true;
case 0x00cd: // L'Í'
case 0x00cc: // L'Ì'
case 0x1ec8: // L'Ỉ'
case 0x0128: // L'Ĩ'
case 0x1eca: // L'Ị'
*letter = (char)'I';
return true;
case 0x00ed: // L'í'
case 0x00ec: // L'ì'
case 0x1ec9: // L'ỉ'
case 0x0129: // L'ĩ'
case 0x1ecb: // L'ị'
*letter = (char)'i';
return true;
case 0x00d3: // L'Ó'
case 0x00d2: // L'Ò'
case 0x1ece: // L'Ỏ'
case 0x00d5: // L'Õ'
case 0x1ecc: // L'Ọ'
*letter = (char)'O';
return true;
case 0x00f3: // L'ó'
case 0x00f2: // L'ò'
case 0x1ecf: // L'ỏ'
case 0x00f5: // L'õ'
case 0x1ecd: // L'ọ'
*letter = (char)'o';
return true;
case 0x1ed0: // L'Ố'
case 0x1ed2: // L'Ồ'
case 0x1ed4: // L'Ổ'
case 0x1ed6: // L'Ỗ'
case 0x1ed8: // L'Ộ'
*letter = (char)0xd4;
return true;
case 0x1ed1: // L'ố'
case 0x1ed3: // L'ồ'
case 0x1ed5: // L'ổ'
case 0x1ed7: // L'ỗ'
case 0x1ed9: // L'ộ'
*letter = (char)0xf4;
return true;
case 0x1eda: // L'Ớ'
case 0x1edc: // L'Ờ'
case 0x1ede: // L'Ở'
case 0x1ee0: // L'Ỡ'
case 0x1ee2: // L'Ợ'
*letter = (char)0xd5;
return true;
case 0x1edb: // L'ớ'
case 0x1edd: // L'ờ'
case 0x1edf: // L'ở'
case 0x1ee1: // L'ỡ'
case 0x1ee3: // L'ợ'
*letter = (char)0xf5;
return true;
case 0x00da: // L'Ú'
case 0x00d9: // L'Ù'
case 0x1ee6: // L'Ủ'
case 0x0168: // L'Ũ'
case 0x1ee4: // L'Ụ'
*letter = (char)'U';
return true;
case 0x00fa: // L'ú'
case 0x00f9: // L'ù'
case 0x1ee7: // L'ủ'
case 0x0169: // L'ũ'
case 0x1ee5: // L'ụ'
*letter = (char)'u';
return true;
case 0x1ee8: // L'Ứ'
case 0x1eea: // L'Ừ'
case 0x1eec: // L'Ử'
case 0x1eee: // L'Ữ'
case 0x1ef0: // L'Ự'
*letter = (char)0xdd;
return true;
case 0x1ee9: // L'ứ'
case 0x1eeb: // L'ừ'
case 0x1eed: // L'ử'
case 0x1eef: // L'ữ'
case 0x1ef1: // L'ự'
*letter = (char)0xfd;
return true;
case 0x1ef2: // L'Ỳ'
case 0x00dd: // L'Ý'
case 0x1ef6: // L'Ỷ'
case 0x1ef8: // L'Ỹ'
case 0x1ef4: // L'Ỵ'
*letter = (char)'Y';
return true;
case 0x1ef3: // L'ỳ'
case 0x00fd: // L'ý'
case 0x1ef7: // L'ỷ'
case 0x1ef9: // L'ỹ'
case 0x1ef5: // L'ỵ'
*letter = (char)'y';
return true;
case 0x0102: // L'Ă'
*letter = (char)0xc3;
return true;
case 0x0103: // L'ă'
*letter = (char)0xe3;
return true;
case 0x0110: // L'Đ'
*letter = (char)0xd0;
return true;
case 0x0111: // L'đ'
*letter = (char)0xf0;
return true;
case 0x01a0: // L'Ơ'
*letter = (char)0xd5;
return true;
case 0x01a1: // L'ơ'
*letter = (char)0xf5;
return true;
case 0x01af: // L'Ư'
*letter = (char)0xdd;
return true;
case 0x01b0: // L'ư'
*letter = (char)0xfd;
return true;
case 0x20ab: // L'₫'
*letter = (char)0xfe;
return true;
case 0x201c: // L'“'
*letter = (char)'"';
return true;
case 0x201d: // L'”'
*letter = (char)'"';
return true;
}
if(input < 256)
{
*letter = (char)input;
return true;
}
return false;
}
static bool DecomposeTone(wchar_t input, char* tone)
{
switch(input)
{
case 0x00c1: // L'Á'
case 0x00e1: // L'á'
case 0x1eae: // L'Ắ'
case 0x1eaf: // L'ắ'
case 0x1ea4: // L'Ấ'
case 0x1ea5: // L'ấ'
case 0x00c9: // L'É'
case 0x00e9: // L'é'
case 0x1ebe: // L'Ế'
case 0x1ebf: // L'ế'
case 0x00cd: // L'Í'
case 0x00ed: // L'í'
case 0x00d3: // L'Ó'
case 0x00f3: // L'ó'
case 0x1ed0: // L'Ố'
case 0x1ed1: // L'ố'
case 0x1eda: // L'Ớ'
case 0x1edb: // L'ớ'
case 0x00da: // L'Ú'
case 0x00fa: // L'ú'
case 0x1ee8: // L'Ứ'
case 0x1ee9: // L'ứ'
case 0x00dd: // L'Ý'
case 0x00fd: // L'ý'
*tone = (char)0xec;
return true;
case 0x00c0: // L'À'
case 0x00e0: // L'à'
case 0x1eb0: // L'Ằ'
case 0x1eb1: // L'ằ'
case 0x1ea6: // L'Ầ'
case 0x1ea7: // L'ầ'
case 0x00c8: // L'È'
case 0x00e8: // L'è'
case 0x1ec0: // L'Ề'
case 0x1ec1: // L'ề'
case 0x00cc: // L'Ì'
case 0x00ec: // L'ì'
case 0x00d2: // L'Ò'
case 0x00f2: // L'ò'
case 0x1ed2: // L'Ồ'
case 0x1ed3: // L'ồ'
case 0x1edc: // L'Ờ'
case 0x1edd: // L'ờ'
case 0x00d9: // L'Ù'
case 0x00f9: // L'ù'
case 0x1eea: // L'Ừ'
case 0x1eeb: // L'ừ'
case 0x1ef2: // L'Ỳ'
case 0x1ef3: // L'ỳ'
*tone = (char)0xcc;
return true;
case 0x1ea2: // L'Ả'
case 0x1ea3: // L'ả'
case 0x1eb2: // L'Ẳ'
case 0x1eb3: // L'ẳ'
case 0x1ea8: // L'Ẩ'
case 0x1ea9: // L'ẩ'
case 0x1eba: // L'Ẻ'
case 0x1ebb: // L'ẻ'
case 0x1ec2: // L'Ể'
case 0x1ec3: // L'ể'
case 0x1ec8: // L'Ỉ'
case 0x1ec9: // L'ỉ'
case 0x1ece: // L'Ỏ'
case 0x1ecf: // L'ỏ'
case 0x1ed4: // L'Ổ'
case 0x1ed5: // L'ổ'
case 0x1ede: // L'Ở'
case 0x1edf: // L'ở'
case 0x1ee6: // L'Ủ'
case 0x1ee7: // L'ủ'
case 0x1eec: // L'Ử'
case 0x1eed: // L'ử'
case 0x1ef6: // L'Ỷ'
case 0x1ef7: // L'ỷ'
*tone = (char)0xd2;
return true;
case 0x00c3: // L'Ã'
case 0x00e3: // L'ã'
case 0x1eb4: // L'Ẵ'
case 0x1eb5: // L'ẵ'
case 0x1eaa: // L'Ẫ'
case 0x1eab: // L'ẫ'
case 0x1ebc: // L'Ẽ'
case 0x1ebd: // L'ẽ'
case 0x1ec4: // L'Ễ'
case 0x1ec5: // L'ễ'
case 0x0128: // L'Ĩ'
case 0x0129: // L'ĩ'
case 0x00d5: // L'Õ'
case 0x00f5: // L'õ'
case 0x1ed6: // L'Ỗ'
case 0x1ed7: // L'ỗ'
case 0x1ee0: // L'Ỡ'
case 0x1ee1: // L'ỡ'
case 0x0169: // L'ũ'
case 0x0168: // L'Ũ'
case 0x1eee: // L'Ữ'
case 0x1eef: // L'ữ'
case 0x1ef8: // L'Ỹ'
case 0x1ef9: // L'ỹ'
*tone = (char)0xde;
return true;
case 0x1ea0: // L'Ạ'
case 0x1ea1: // L'ạ'
case 0x1eb6: // L'Ặ'
case 0x1eb7: // L'ặ'
case 0x1eac: // L'Ậ'
case 0x1ead: // L'ậ'
case 0x1eb8: // L'Ẹ'
case 0x1eb9: // L'ẹ'
case 0x1ec6: // L'Ệ'
case 0x1ec7: // L'ệ'
case 0x1eca: // L'Ị'
case 0x1ecb: // L'ị'
case 0x1ecc: // L'Ọ'
case 0x1ecd: // L'ọ'
case 0x1ed8: // L'Ộ'
case 0x1ed9: // L'ộ'
case 0x1ee2: // L'Ợ'
case 0x1ee3: // L'ợ'
case 0x1ee4: // L'Ụ'
case 0x1ee5: // L'ụ'
case 0x1ef0: // L'Ự'
case 0x1ef1: // L'ự'
case 0x1ef4: // L'Ỵ'
case 0x1ef5: // L'ỵ'
*tone = (char)0xf2;
return true;
}
return false;
}
int EL_String_Encode_Vietnamese(const wchar_t* wide, int wideLen, char* multi, int multiLen)
{
if(wideLen < 0)
wideLen = (int)wcslen(wide) + 1;
int src = 0;
int dest = 0;
if(wideLen > 0)
{
while(src < wideLen && dest < multiLen)
{
char letter;
if(DecomposeLetter(wide[src], &letter))
{
multi[dest++] = letter;
}
char tone;
if(DecomposeTone(wide[src], &tone) && dest < multiLen)
{
multi[dest++] = tone;
}
++src;
}
}
return dest;
}
@@ -0,0 +1,4 @@
#pragma once
int EL_String_Decode_Vietnamese(const char* multi, int multiLen, wchar_t* wide, int wideLen);
int EL_String_Encode_Vietnamese(const wchar_t* wide, int wideLen, char* multi, int multiLen);
+198
View File
@@ -11,6 +11,10 @@
#include <thread>
#include <vector>
#if __has_include(<iconv.h>)
#include <iconv.h>
#define MT_WIN32CRT_HAS_ICONV 1
#endif
#include <glob.h>
#include <sys/stat.h>
#include <unistd.h>
@@ -165,4 +169,198 @@ DWORD GetTickCount()
return timeGetTime();
}
namespace {
// Windows-1252 0x80..0x9F; the other bytes map to the same code point (0 marks the undefined ones).
const char32_t kCp1252High[32] = {
0x20AC, 0, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0, 0x017D, 0,
0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0, 0x017E, 0x0178,
};
bool is_cp1252(UINT cp) { return cp == CP_ACP || cp == 1252; }
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))); }
}
#if defined(MT_WIN32CRT_HAS_ICONV)
std::string iconv_name(UINT cp)
{
switch (cp) {
case 949: return "CP949";
case 936: return "GBK";
case 950: return "BIG5";
case 932: return "SHIFT_JIS";
case 874: return "CP874";
default: return "CP" + std::to_string(cp);
}
}
// One character at a time, so an undecodable byte becomes one default char like Windows does.
bool iconv_decode(UINT cp, const unsigned char* p, size_t n, std::u32string& out)
{
iconv_t cd = iconv_open("UTF-32LE", iconv_name(cp).c_str());
if (cd == reinterpret_cast<iconv_t>(-1)) return false;
for (size_t i = 0; i < n;) {
unsigned char wide[16];
bool done = false;
for (size_t len = 1; len <= 4 && i + len <= n && !done; ++len) {
iconv(cd, nullptr, nullptr, nullptr, nullptr);
char* in = const_cast<char*>(reinterpret_cast<const char*>(p + i));
size_t in_left = len;
char* o = reinterpret_cast<char*>(wide);
size_t o_left = sizeof(wide);
if (iconv(cd, &in, &in_left, &o, &o_left) != size_t(-1) && in_left == 0 && o_left < sizeof(wide)) {
for (size_t k = 0; k + 4 <= sizeof(wide) - o_left; k += 4)
out.push_back(char32_t(wide[k] | (wide[k + 1] << 8) | (wide[k + 2] << 16) | (unsigned(wide[k + 3]) << 24)));
i += len;
done = true;
}
}
if (!done) { out.push_back(0xFFFD); ++i; }
}
iconv_close(cd);
return true;
}
bool iconv_encode(UINT cp, const std::u32string& in, std::string& out, bool* used_default)
{
iconv_t cd = iconv_open(iconv_name(cp).c_str(), "UTF-32LE");
if (cd == reinterpret_cast<iconv_t>(-1)) return false;
for (char32_t c : in) {
unsigned char src[4] = {static_cast<unsigned char>(c), static_cast<unsigned char>(c >> 8), static_cast<unsigned char>(c >> 16), static_cast<unsigned char>(c >> 24)};
char buf[16];
char* ip = reinterpret_cast<char*>(src);
size_t il = 4;
char* op = buf;
size_t ol = sizeof(buf);
iconv(cd, nullptr, nullptr, nullptr, nullptr);
if (iconv(cd, &ip, &il, &op, &ol) != size_t(-1) && il == 0) {
out.append(buf, sizeof(buf) - ol);
} else {
out.push_back('?');
*used_default = true;
}
}
iconv_close(cd);
return true;
}
#endif
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 (is_cp1252(cp)) {
for (size_t i = 0; i < n; ++i) {
const unsigned char c = p[i];
out.push_back(c >= 0x80 && c < 0xA0 ? (kCp1252High[c - 0x80] ? kCp1252High[c - 0x80] : char32_t(c)) : char32_t(c));
}
return true;
}
#if defined(MT_WIN32CRT_HAS_ICONV)
if (iconv_decode(cp, p, n, out)) return true;
#endif
// No converter 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, 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 (is_cp1252(cp)) {
for (char32_t c : in) {
if (c < 0x80 || (c >= 0xA0 && c < 0x100)) { out.push_back(char(c)); continue; }
int hit = -1;
for (int k = 0; k < 32; ++k)
if (kCp1252High[k] == c) { hit = k; break; }
if (hit >= 0) out.push_back(char(0x80 + hit));
else { out.push_back('?'); *used_default = true; }
}
return;
}
#if defined(MT_WIN32CRT_HAS_ICONV)
if (iconv_encode(cp, in, out, used_default)) return;
#endif
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, 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, 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, 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());
}
#endif // !_WIN32
+12
View File
@@ -83,4 +83,16 @@ void DeleteCriticalSection(LPCRITICAL_SECTION cs);
void EnterCriticalSection(LPCRITICAL_SECTION cs);
void LeaveCriticalSection(LPCRITICAL_SECTION cs);
// stringapiset.h. WCHAR is 32-bit here, so wide strings hold UTF-32 code points. CP_UTF8 and
// CP_ACP/1252 are converted directly; other code pages go through iconv where the platform has it.
#define CP_ACP 0
#define CP_UTF8 65001
#define MB_PRECOMPOSED 0x00000001
#define MB_ERR_INVALID_CHARS 0x00000008
#define WC_NO_BEST_FIT_CHARS 0x00000400
int MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte,
LPWSTR lpWideCharStr, int cchWideChar);
int WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar,
LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar);
#endif // !_WIN32
@@ -1,10 +1,13 @@
#pragma once
// Shim for <windows.h> on non-Windows targets: the Win32 scalar types and CRT spellings
// 40250 sources use (common/Win32Types.h, common/Win32Crt.h), plus the headers the real one pulls
// in that 40250 relies on transitively (winnt.h brings <string.h> for memcpy/memset).
// in that 40250 relies on transitively (winnt.h brings <string.h> for memcpy/memset, and <wchar.h>
// for wcslen).
#include <string.h>
#include <wchar.h>
#include "../../Win32Types.h"
#include "../../Win32Crt.h"
#include "wingdi.h"
// winuser.h / wingdi.h constants named in 40250 declarations (default arguments).
#define WS_OVERLAPPED 0x00000000L
@@ -0,0 +1,64 @@
#pragma once
// Shim for the <wingdi.h> subset the 40250 font path uses (EterLib/GrpDIB, GrpFontTexture, Util).
// The calls are implemented by the platform layer over FreeType (platform/Win32Gdi.cpp): one DIB
// per memory DC, glyphs rendered the way GDI lays out a TrueType face for a LOGFONT cell height.
#include "../../Win32Types.h"
typedef DWORD COLORREF;
typedef void* HGDIOBJ;
#define RGB(r, g, b) ((COLORREF)(((BYTE)(r) | ((WORD)((BYTE)(g)) << 8)) | (((DWORD)(BYTE)(b)) << 16)))
typedef struct _ABCFLOAT
{
FLOAT abcfA;
FLOAT abcfB;
FLOAT abcfC;
} ABCFLOAT, *LPABCFLOAT;
#define TRANSPARENT 1
#define OPAQUE 2
#define FW_NORMAL 400
#define FW_BOLD 700
#define OUT_DEFAULT_PRECIS 0
#define CLIP_DEFAULT_PRECIS 0
#define DEFAULT_QUALITY 0
#define NONANTIALIASED_QUALITY 3
#define ANTIALIASED_QUALITY 4
#define DEFAULT_PITCH 0
#define ANSI_CHARSET 0
#define DEFAULT_CHARSET 1
#define SHIFTJIS_CHARSET 128
#define HANGUL_CHARSET 129
#define GB2312_CHARSET 134
#define CHINESEBIG5_CHARSET 136
#define GREEK_CHARSET 161
#define TURKISH_CHARSET 162
#define VIETNAMESE_CHARSET 163
#define HEBREW_CHARSET 177
#define ARABIC_CHARSET 178
#define BALTIC_CHARSET 186
#define RUSSIAN_CHARSET 204
#define THAI_CHARSET 222
#define EASTEUROPE_CHARSET 238
#define BI_RGB 0L
#define DIB_RGB_COLORS 0
HDC CreateCompatibleDC(HDC hdc);
BOOL DeleteDC(HDC hdc);
HBITMAP CreateDIBSection(HDC hdc, const BITMAPINFO* pbmi, UINT usage, void** ppvBits, HANDLE hSection, DWORD offset);
int SetDIBitsToDevice(HDC hdc, int xDest, int yDest, DWORD w, DWORD h, int xSrc, int ySrc, UINT StartScan,
UINT cLines, const void* lpvBits, const BITMAPINFO* lpbmi, UINT ColorUse);
HFONT CreateFontIndirect(const LOGFONT* lplf);
BOOL DeleteObject(HGDIOBJ ho);
HGDIOBJ SelectObject(HDC hdc, HGDIOBJ h);
COLORREF SetTextColor(HDC hdc, COLORREF color);
COLORREF SetBkColor(HDC hdc, COLORREF color);
int SetBkMode(HDC hdc, int mode);
BOOL GetTextExtentPoint32W(HDC hdc, LPCWSTR lpString, int c, LPSIZE psizl);
BOOL GetCharABCWidthsFloatW(HDC hdc, UINT iFirst, UINT iLast, LPABCFLOAT lpABC);
BOOL TextOutW(HDC hdc, int x, int y, LPCWSTR lpString, int c);
BOOL TextOutA(HDC hdc, int x, int y, LPCSTR lpString, int c);
#define TextOut TextOutA
+23
View File
@@ -12,7 +12,9 @@
#include <string>
#endif
#include <godot_cpp/classes/image.hpp>
#include <godot_cpp/variant/dictionary.hpp>
#include <godot_cpp/variant/packed_byte_array.hpp>
using namespace godot;
@@ -97,6 +99,24 @@ Array Metin2PythonHost::ui_render_commands() {
return out;
}
Ref<Image> Metin2PythonHost::memory_texture(const String &name, int64_t known_revision) {
UIMemoryTexture texture;
if (!UIRenderMemoryTexture(name.utf8().get_data(), &texture) || texture.width <= 0 || texture.height <= 0)
return Ref<Image>();
if (known_revision >= 0 && texture.revision == static_cast<std::uint32_t>(known_revision))
return Ref<Image>();
PackedByteArray rgba;
rgba.resize(static_cast<int64_t>(texture.argb.size()) * 4);
uint8_t *out = rgba.ptrw();
for (std::uint32_t argb : texture.argb) {
*out++ = static_cast<uint8_t>(argb >> 16);
*out++ = static_cast<uint8_t>(argb >> 8);
*out++ = static_cast<uint8_t>(argb);
*out++ = static_cast<uint8_t>(argb >> 24);
}
return Image::create_from_data(texture.width, texture.height, false, Image::FORMAT_RGBA8, rgba);
}
void Metin2PythonHost::stop() {
PythonBoot::Stop();
}
@@ -128,6 +148,7 @@ void Metin2PythonHost::ui_mouse_button(int, bool, int, int) {}
void Metin2PythonHost::ui_key(int, bool) {}
void Metin2PythonHost::ui_update() {}
Array Metin2PythonHost::ui_render_commands() { return Array(); }
Ref<Image> Metin2PythonHost::memory_texture(const String &, int64_t) { return Ref<Image>(); }
void Metin2PythonHost::stop() {
}
@@ -148,6 +169,8 @@ void Metin2PythonHost::_bind_methods() {
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_key", "key", "pressed"), &Metin2PythonHost::ui_key);
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_update"), &Metin2PythonHost::ui_update);
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_render_commands"), &Metin2PythonHost::ui_render_commands);
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("memory_texture", "name", "known_revision"),
&Metin2PythonHost::memory_texture, DEFVAL(-1));
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("stop"), &Metin2PythonHost::stop);
}
+5
View File
@@ -15,6 +15,8 @@
#include <godot_cpp/classes/object.hpp>
#include <godot_cpp/variant/string.hpp>
#include <godot_cpp/variant/array.hpp>
#include <godot_cpp/classes/image.hpp>
#include <godot_cpp/classes/ref.hpp>
namespace mtgodot {
@@ -38,6 +40,9 @@ public:
static void ui_key(int key, bool pressed);
static void ui_update();
static godot::Array ui_render_commands();
// The pixels of a "mem:<id>@<revision>" image command (the CGraphicFontTexture glyph pages) as an
// RGBA8 Image; null when the texture is gone or its revision is still `known_revision`.
static godot::Ref<godot::Image> memory_texture(const godot::String &name, int64_t known_revision);
static void stop();
protected: