Skip to content

In-Game: [HTML5] buffer_poke() misses bytes when printing UTF-8 characters #14395

@gm-bug-reporter

Description

@gm-bug-reporter

Description

When poking a string into buffer using buffer_poke, it can miss some bytes. This happens whenever string should contain UTF-8 charactes longer than 1 byte, such as "¤" or other non-ASCII characters.

The problem arises that JavaScript uses UTF-16 characters instead, and buffer_poke uses this string directly for reading its length and bytes. It should instead convert UTF-16 string into UTF-8, or something, so getting bytes is done properly.

This problem is not appearent in buffer_write, as it handles string conversion into UTF8 correctly. Compare the implementation of these both here:

buffer_write:
https://github.com/YoYoGames/GameMaker-HTML5/blob/8f87db22cefd0cb1d9e706523730946e1319a117/scripts/yyBuffer.js#L1309

buffer_poke:
https://github.com/YoYoGames/GameMaker-HTML5/blob/8f87db22cefd0cb1d9e706523730946e1319a117/scripts/yyBuffer.js#L1727

Here are direct code snippets from both.
First buffer_poke, note that .charCodeAtis done directly at the _value which is function argument.

case eBuffer_String:
case eBuffer_Text:
    {               
        for (var i = 0; i < _value.length; i++) {
            var charCode = _value.charCodeAt(i) & 0xff;   // Now UTF8, so only a byte in size
            this.m_DataView.setUint8(_offset++, charCode, true);
        }
        // "text" mode doesn't add a NULL at the end.
        if (_type === eBuffer_String) {
            this.m_DataView.setUint8(_offset++, 0, true);
        }
        this.yyb_UpdateUsedSize(_offset);
    }
    return;

On other hand, implementation of buffer_writepreprocesses the string into UTF8 as seen here:

case eBuffer_String:
case eBuffer_Text:
    {               
        for (var i = 0; i < UTF8_String.length; i++) {
            var charCode = UTF8_String.charCodeAt(i) & 0xff;   // Now UTF8, so only a byte in size
            this.m_DataView.setUint8(this.m_BufferIndex++, charCode, true);
        }
        // "text" mode doesn't add a NULL at the end.
        if (_type === eBuffer_String) {
            this.m_DataView.setUint8(this.m_BufferIndex++, 0, true);
        }
    }
    break;

The conversion is done earlier with this piece of code:

var sizeneeded = BufferSizeOf(_type);
if( ( _type === eBuffer_String ) || ( _type === eBuffer_Text ) ){
    UTF8_String = UnicodeToUTF8(_value);
    sizeneeded = UTF8_String.length;
    if( _type === eBuffer_String )  sizeneeded++;  // null at the end of a string (not text)
}

The buffer poke doesn't do this.

Steps To Reproduce

  1. Run example project in Windows
  2. Compare console output -> Should all be the same.
  3. Run example project in HTML5 export
  4. Compare console output -> buffer_poke differs (misses bytes)

Which version of GameMaker are you reporting this issue for?

IDE v2024.1400.4.1023 Runtime v2024.1400.4.998

Which operating system(s) are you seeing the problem on?

Windows 10.0.19045.0

27b45cec-74fa-4735-8f0c-fe3c06861af5

Metadata

Metadata

Assignees

Labels

gms2-bugIn-game bugs with the "GameMaker Studio 2" runtimesprojectThis issue has a sample project attached

Projects

Status

Ready for QA

Relationships

None yet

Development

No branches or pull requests

Issue actions