Episode 5.4 Vietnamese language support (game.exe)

Bowie

Well-known member
Developer
Joined
Sep 30, 2023
Messages
107
First of all, I would like to thank @darren.tran and @Lothbrok for their help. They answered my questions and tested the solution to make sure it works. This was a team effort. We will be using this client from the archive:

https://archive.openshaiya.org/shaiya-us/clients/ps0183-22-12-2011-game.exe

There used to be a language setting in the configuration file. A number was assigned to a global variable, depending on the key value.

C++:
enum struct LoginVersion : UINT32
{
    Korea = 1,     //
    China,         //
    Vietnam,       // 65001 (UTF-8)
    Japan,         // 932
    Taiwan,        // 950
    English,       // CP_ACP
    Germany,       // CP_ACP
    SingaMala,     // 950
    HongKong,      //
    France,        // CP_ACP
    Russia,        // CP_ACP
    Turkey,        // 1254
    Brazil,        // CP_ACP
    LatinAmerica,  // CP_ACP
    Poland,        // 1250
    Italy,         // CP_ACP
    Philippines    // CP_ACP
};

Code:
// winnls.h.
#define CP_ACP 0 // default to ANSI code page

See the following documentation to learn more:

MultiByteToWideChar
WideCharToMultiByte
https://learn.microsoft.com/en-us/windows/win32/intl/code-page-identifiers

Depending on the language number, a switch returns a code page. The function can be found at address 0x40A550.

Capture.PNG

Case 3 (Vietnam) returns 65001 (UTF-8). Since we are using a US client, the number will be 6 (English). We will change the number at address 00708598 from 6 to 3.

Capture3.PNG

Changing the number will affect several things. It appears they wrote features for particular regions. What is relevant to this guide is the fonts. Each language has its own fonts, which vary in height, weight, etc. The language value is compared to determine which block to execute. Follow the jump instructions until you see 3 (Vietnam).

Capture2.PNG

Below the call to CreateFontA it creates 3 more fonts. The values 0x190 and 0x2BC are weight macros.

C++:
// wingdi.h
/* Font Weights */
#define FW_DONTCARE         0
#define FW_THIN             100
#define FW_EXTRALIGHT       200
#define FW_LIGHT            300
#define FW_NORMAL           400 // 0x190
#define FW_MEDIUM           500
#define FW_SEMIBOLD         600
#define FW_BOLD             700 // 0x2BC
#define FW_EXTRABOLD        800
#define FW_HEAVY            900

Now, we need to embed the manifest. Please see the following documentation:

https://learn.microsoft.com/en-us/windows/apps/design/globalizing/use-utf8-code-page
https://devblogs.microsoft.com/oldnewthing/20220531-00/?p=106697
https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests#activecodepage

If you've added additional sections to the file, embedding the manifest may break the executable. Embed the manifest before doing anything else. I'm sure there is more than one way to do this, so find something that works for you.

Put the following manifest file in the same directory as game.exe:

XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <assemblyIdentity type="win32" name="..." version="6.0.0.0"/>
  <application>
    <windowsSettings>
      <activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
    </windowsSettings>
  </application>
</assembly>

The Windows SDK includes a program named mt.exe, which can be used to embed the manifest. You will need to add the path to your system environment variables so the system can find the file. The path will look something like this:

Code:
C:\Program Files (x86)\Windows Kits\10\bin\10.0.20348.0\x86

Execute the following command to embed the manifest:

Code:
mt.exe -manifest utf-8.manifest -outputresource:game.exe;#1

You can use Resource Hacker to check the result. It should also be able to embed the manifest, but I didn't test it.

Capture4.PNG

Notes

Login input limit:
Japan (4): 12
Russia (11): 31
Default: 19

Chat input limit:
Taiwan (5): 70
Hong Kong (9): 70
Default: 120

Note: the chat color format is 8 characters.
 
Last edited:
Great work, I'm sure this will be helpful to many people who are looking to create non-english servers. Perhaps it may be beneficial to also look into expanding the source array for chat messages as by default they are capped at 128 bytes, and as far as I'm aware, most Vietnamese characters (and non-english languages) use at least 2 bytes to encode the rune. UTF-8, despite what the name implies, uses up to 4-bytes per character in some cases.
 
I found some input buffers when I was looking at the memory, but I didn't add them to the repo. I need some more time to wrap my head around this stuff. I wasn't even aware of the problem until Garret told me about it.
 
Last edited:
Dear Bowie,

First and foremost, I want to express my deep appreciation for your heartfelt post on the Shaiya forum!

I would like to share some thoughts on the impact of your post. By publishing this content, it unintentionally affected the development efforts of other developers. I believe this was not your intention, but the competition in other countries is quite intense due to a limited player base, not as extensive as the EU community. This creates a more challenging competitive environment and requires mutual respect among developers.

This developers to have the space to develop and compete in a healthy manner and negatively affecting the Asian community.

Once again, I sincerely thank you for your unwavering contributions to the Shaiya community. Your efforts and passion are always recognized and appreciated.

Best regards !
 
Last edited:
Anyone saying that someone shouldn't share something because it disrupts developers in other regions is just childish. It is not the fault of our community that foreign developers don’t cooperate with each other, or with us. The goal of this community is to create a better environment for Shaiya players, and I'm all for anything that advances that goal. If you do not agree, you do not belong here.
 
Last edited:
I added a link to a blog post that explains how to use the manifest for code pages other than UTF-8. The author explains default behavior too.
 
Bowie thân mến,

Trước hết, tôi muốn bày tỏ lòng biết ơn sâu sắc của tôi đối với bài viết chân thành của bạn trên diễn đàn Shaiya!

Tôi muốn chia sẻ một số suy nghĩ về tác động của bài đăng của bạn. Bằng cách xuất bản nội dung này, nó vô tình ảnh hưởng đến nỗ lực phát triển của các nhà phát triển khác. Tôi tin rằng đây không phải là ý định của bạn, nhưng sự cạnh tranh ở các quốc gia khác khá gay gắt do lượng người chơi hạn chế, không rộng rãi như cộng đồng EU. Điều này tạo ra một môi trường cạnh tranh đầy thách thức hơn và đòi hỏi sự tôn trọng lẫn nhau giữa các nhà phát triển.

Những nhà phát triển này có không gian để phát triển và cạnh tranh một cách lành mạnh và không gây ảnh hưởng tiêu cực đến cộng đồng người Châu Á.

Một lần nữa, tôi chân thành cảm ơn bạn vì những đóng góp không ngừng nghỉ của bạn cho cộng đồng Shaiya. Những nỗ lực và đam mê của bạn luôn được ghi nhận và trân trọng.

Trân trọng
You are a bad person
 
First of all, I would like to thank @darren.tran and @Lothbrok for their help. They answered my questions and tested the solution to make sure it works. This was a team effort. We will be using this client from the archive:

https://archive.openshaiya.org/shaiya-us/clients/ps0183-22-12-2011-game.exe

There used to be a language setting in the configuration file. A number was assigned to a global variable, depending on the key value.

C++:
enum struct LoginVersion : UINT32
{
    Korea = 1,     //
    China,         //
    Vietnam,       // 65001 (UTF-8)
    Japan,         // 932
    Taiwan,        // 950
    English,       // CP_ACP
    Germany,       // CP_ACP
    SingaMala,     // 950
    HongKong,      //
    France,        // CP_ACP
    Russia,        // CP_ACP
    Turkey,        // 1254
    Brazil,        // CP_ACP
    LatinAmerica,  // CP_ACP
    Poland,        // 1250
    Italy,         // CP_ACP
    Philippines    // CP_ACP
};

Code:
// winnls.h.
#define CP_ACP 0 // default to ANSI code page

See the following documentation to learn more:

MultiByteToWideChar
WideCharToMultiByte
https://learn.microsoft.com/en-us/windows/win32/intl/code-page-identifiers

Depending on the language number, a switch returns a code page. The function can be found at address 0x40A550.

View attachment 79

Case 3 (Vietnam) returns 65001 (UTF-8). Since we are using a US client, the number will be 6 (English). We will change the number at address 00708598 from 6 to 3.

View attachment 81

Changing the number will affect several things. It appears they wrote features for particular regions. What is relevant to this guide is the fonts. Each language has its own fonts, which vary in height, weight, etc. The language value is compared to determine which block to execute. Follow the jump instructions until you see 3 (Vietnam).

View attachment 80

Below the call to CreateFontA it creates 3 more fonts. The values 0x190 and 0x2BC are weight macros.

C++:
// wingdi.h
/* Font Weights */
#define FW_DONTCARE         0
#define FW_THIN             100
#define FW_EXTRALIGHT       200
#define FW_LIGHT            300
#define FW_NORMAL           400 // 0x190
#define FW_MEDIUM           500
#define FW_SEMIBOLD         600
#define FW_BOLD             700 // 0x2BC
#define FW_EXTRABOLD        800
#define FW_HEAVY            900

Now, we need to embed the manifest. Please see the following documentation:

https://learn.microsoft.com/en-us/windows/apps/design/globalizing/use-utf8-code-page
https://devblogs.microsoft.com/oldnewthing/20220531-00/?p=106697
https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests#activecodepage

If you've added additional sections to the file, embedding the manifest may break the executable. Embed the manifest before doing anything else. I'm sure there is more than one way to do this, so find something that works for you.

Put the following manifest file in the same directory as game.exe:

XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <assemblyIdentity loại="win32" tên="..." phiên bản="6.0.0.0"/>
  <ứng dụng>
    <Cài đặt windows>
      <activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
    </windowsCài đặt>
  </ứng dụng>
</lắp ráp><//CODE]

Windows SDK bao gồm một chương trình có tên mt.exe, có thể được sử dụng để nhúng manifest. Bạn sẽ cần thêm đường dẫn vào biến môi trường hệ thống của mình để hệ thống có thể tìm thấy tệp. Đường dẫn sẽ trông giống như thế này:

[MÃ]C:\Program Files (x86)\Windows Kits\10\bin\10.0.20348.0\x86[/MÃ]

Thực hiện lệnh sau để nhúng tệp kê khai:

[MÃ]mt.exe -manifest utf-8.manifest -outputresource:game.exe;#1[/MÃ]

Bạn có thể sử dụng Resource Hacker để kiểm tra kết quả. Nó cũng có thể nhúng manifest, nhưng tôi chưa kiểm tra.

[ATTACH type="full" alt="Capture4.PNG"]82[/ATTACH]

[B]Ghi chú[/B]

Giới hạn đầu vào đăng nhập:
Nhật Bản (4): 12
Nga (11): 31
Mặc định: 19

Giới hạn nhập nội dung trò chuyện:
Đài Loan (5): 70
Hồng Kông (9): 70
Mặc định: 120

Lưu ý: định dạng màu trò chuyện là 8 ký
[/QUOTE]
How to add manifest into game.exe bro ?
 
In a game with Simplified Chinese, skill descriptions and equipment descriptions may cause garbled text if the displayed text length is too long.
 
Back
Top