GameMaker: a buffer pretty-printing function


Output sample

Ever work in GameMaker and find yourself thinking "I wish I could log the contents of this buffer without pausing and staring at it in the debugger"?

The idea

This is one of those things that is pretty straightforward but someone has to write it first - you go over each byte in the buffer and print hexadecimal digits for it ("0".."9" if the digit is <10 or "A".."F" if it is >= 10);

I settle for additional convenience by printing visible characters on the right where available (which is accomplished by stashing them elsewhere and adding that to output once end of row/input is reached);

The whole thing uses a buffer to avoid re-allocating strings more than necessary.

The code

/// @param {buffer} buffer
/// @param {int} ?bytesPerRow
/// @returns {string} text representation
function buffer_prettyprint(_buf, _per_row = 20) {
    static _out = buffer_create(16, buffer_grow, 1);
    static _chars = buffer_create(16, buffer_grow, 1);
    buffer_seek(_out, buffer_seek_start, 0);
    buffer_seek(_chars, buffer_seek_start, 0);
    var _size = buffer_get_size(_buf);
    var _tell = buffer_tell(_buf);
    
    // header:
    buffer_write(_out, buffer_text, "buffer(size: ");
    buffer_write(_out, buffer_text, string(_size));
    buffer_write(_out, buffer_text, ", tell: ");
    buffer_write(_out, buffer_text, string(_tell));
    buffer_write(_out, buffer_text, ", type: ");
    switch (buffer_get_type(_buf)) {
        case buffer_fixed:   buffer_write(_out, buffer_text, "buffer_fixed"  ); break;
        case buffer_grow:    buffer_write(_out, buffer_text, "buffer_grow"   ); break;
        case buffer_fast:    buffer_write(_out, buffer_text, "buffer_fast"   ); break;
        case buffer_wrap:    buffer_write(_out, buffer_text, "buffer_wrap"   ); break;
        case buffer_vbuffer: buffer_write(_out, buffer_text, "buffer_vbuffer"); break;
        default: buffer_write(_out, buffer_text, string(buffer_get_type(_buf))); break;
    }
    buffer_write(_out, buffer_text, "):");
    
    for (var i = 0; i < _size; i++) {
        if (i % _per_row == 0) { // row start
            if (i > 0) { // printable chars
                buffer_write(_out, buffer_text, " | ");
                buffer_write(_chars, buffer_u8, 0);
                buffer_seek(_chars, buffer_seek_start, 0);
                buffer_write(_out, buffer_text, buffer_read(_chars, buffer_string));
                buffer_seek(_chars, buffer_seek_start, 0);
            }
            
            buffer_write(_out, buffer_u8, ord("\n"));
            buffer_write(_out, buffer_text, string_format(i, 6, 0));
            buffer_write(_out, buffer_text, " |");
        }
        buffer_write(_out, buffer_u8, i == _tell ? ord(">") : ord(" "));
        var _byte = buffer_peek(_buf, i, buffer_u8);
        
        // write byte if in printable range or an "unknown" symbol otherwise
        if (_byte >= 32 && _byte < 128) {
            buffer_write(_chars, buffer_u8, _byte);
        } else {
            buffer_write(_chars, buffer_text, "·");
        }
        
        // write the hexadecimal presentation:
        var _hex = _byte >> 4;
        buffer_write(_out, buffer_u8, _hex >= 10 ? ord("A") - 10 + _hex : ord("0") + _hex);
        _hex = _byte & 15;
        buffer_write(_out, buffer_u8, _hex >= 10 ? ord("A") - 10 + _hex : ord("0") + _hex);
    }
    
    // last row's spaces and printable chars:
    if (_size % _per_row != 0) {
        repeat (_per_row - (_size % _per_row)) {
            buffer_write(_out, buffer_text, "   ");
        }
    }
    buffer_write(_out, buffer_text, " | ");
    buffer_write(_chars, buffer_u8, 0);
    buffer_seek(_chars, buffer_seek_start, 0);
    buffer_write(_out, buffer_text, buffer_read(_chars, buffer_string));
    
    buffer_write(_out, buffer_u8, 0);
    buffer_seek(_out, buffer_seek_start, 0);
    return buffer_read(_out, buffer_text);
}

Example

The function can be called with a single buffer argument or with a second argument indicating number of bytes to be shown per row.

var b = buffer_create(104, buffer_fixed, 1);
buffer_write(b, buffer_string, "hello there!")
show_debug_message(buffer_prettyprint(b));
show_debug_message(buffer_prettyprint(b, 16));

Output:

buffer(size: 104, tell: 13, type: buffer_fixed):
     0 | 68 65 6C 6C 6F 20 74 68 65 72 65 21 00>00 00 00 00 00 00 00 | hello there!········
    20 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ····················
    40 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ····················
    60 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ····················
    80 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ····················
   100 | 00 00 00 00                                                 | ····
buffer(size: 104, tell: 13, type: buffer_fixed):
     0 | 68 65 6C 6C 6F 20 74 68 65 72 65 21 00>00 00 00 | hello there!····
    16 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ················
    32 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ················
    48 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ················
    64 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ················
    80 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ················
    96 | 00 00 00 00 00 00 00 00                         | ········

That is all

Related posts:

2 thoughts on “GameMaker: a buffer pretty-printing function

  1. Hello,

    I sent you an email with these questions, but I’m posting them here as well just in case you don’t see the email. Sorry that this is completely unrelated to this blog post, but this is the most recent activity from you that I could find, and my issue is kind of urgent.

    Email begins here:

    I stumbled upon your solution to Game Maker halting a running game if its window frame is being selected/dragged (see link below), and it’s perfect for what I need. But I have a few questions about it:

    1. I’m aware that in addition to stopping the game window from freezing, this extension makes it so that the frame of the game window can be resized, maximized, etc. For my game though, I want it so that the window cannot be resized/maximized at all (except for perhaps borderless fullscreen). How can I disable this resizability? (I’m a bit newer to using Game Maker extensions, sorry if there is an easy solution that I’m not seeing).

    2. My game is a rhythm game, so it’s vital that the movement of certain objects stays in sync with the audio. But, when the window freezes, the audio continues to play, so everything ends up out of sync (which is why I went looking for a solution like yours in the first place). If I could have the game detect if the window has been frozen, I could solve my problem by simply pausing the audio whenever the window is frozen, and then resuming it whenever the window becomes unfrozen again (considering that you said that your fix was pretty unstable, this seems like a simpler, stabler solution). So, do you know if it is possible for the game to detect when the window has been frozen?

    3. As I mentioned earlier, I noticed you said that this extension is pretty unpredictable, and that it shouldn’t be used if an alternative solution can be found. Are there any other solutions/extensions out there that you know of that solve this issue that are more stable?

    Thanks in advance!
    Noah Friedel

    Link to your post about the extension I’m talking about:
    https://yellowafterlife.itch.io/window-freeze-fix

Leave a Reply to Noah Friedel Cancel reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.