The goal of this DLL is to create a platform that other Game Maker DLLs can use to exchange binary data. Binary data can be written to streams and buffers and read back later. Streams and buffers can also be used directly in Game Maker (in GML), so it's also possible to read or write binary data manually.
This DLL provides little functionality on its own, it is designed to be used together with other DLLs that support Shared Stream Buffers.
This DLL supports a number of different data types. The table below lists the most important properties of each type.
Type | Size (bytes) | Lowest value | Highest value | Resolution | Other names |
int8 | 1 | -128 | 127 | 1 | byte, char |
uint8 | 1 | 0 | 255 | 1 | unsigned byte, unsigned char |
int16 | 2 | -32.768 | 32.767 | 1 | short |
uint16 | 2 | 0 | 65.535 | 1 | unsigned short, WORD |
int32 | 4 | -2.147.483.648 | 2.147.483.647 | 1 | int |
uint32 | 4 | 0 | 4.294.967.296 | 1 | unsigned int, DWORD |
int64 | 8 | -9.223.372.036.854.775.808 | 9.223.372.036.854.775.807 | 1 | long long |
uint64 | 8 | 0 | 18.446.744.073.709.551.615 | 1 | unsigned long long, QWORD |
float32 | 4 | -1.7014e38 | 1.7014e38 | ~8 significant figures | float |
float64 | 8 | -8.9885e307 | 8.9885e307 | ~16 significant figures | double |
string | length+1 | - | - | - | NULL-terminated string, C string |
Streams are data structures that allow reading and writing of binary data. There are multiple types of streams. What happens with this data depends on the type of stream. A stream could be a simple memory buffer, but also a socket, for example. Other DLLs can add new types of streams.
If you try to read data from a stream when there isn't any data available, the function will return a default value. The value depends on the type of data you're reading:
Numbers: The default value is 0.
Strings: The default value is an empty string.
Raw data: The default value is a block of null bytes (if you try to read 4 bytes, the result is 4 bytes with a value of 0, which evaluates to an empty string in GML).
Hexadecimal data: The default value is a block of zero characters (e.g. if you try to read 4 bytes, the result is "00000000")
stream_exists(id) - Returns whether the stream with the given id exists. This function will also work for buffers (as all buffers are also streams).
stream_read_int8(id) stream_read_uint8(id) stream_read_int16(id) stream_read_uint16(id) stream_read_int32(id) stream_read_uint32(id) stream_read_int64(id) stream_read_uint64(id) stream_read_float32(id) stream_read_float64(id)
Reads a number from the stream.
stream_write_int8(id, value) stream_write_uint8(id, value) stream_write_int16(id, value) stream_write_uint16(id, value) stream_write_int32(id, value) stream_write_uint32(id, value) stream_write_int64(id, value) stream_write_uint64(id, value) stream_write_float32(id, value) stream_write_float64(id, value)
Writes a number to the stream.
stream_read_string(id) - Reads a (null-terminated) string from the stream.
stream_write_string(id, string) - Writes a (null-terminated) string to the stream.
stream_read_data(id, bytes) - Reads raw data from the stream. This won't work correctly if the raw data contains null bytes (bytes with a value of 0), because Game Maker thinks the null byte marks the end of the string. Only use this if you know the raw data doesn't contain null bytes.
stream_write_data(id, string) - Writes raw data to the stream. This function is almost identical to buffer_write_string, but this function will not write a null byte to mark the end of the string. This means you have to know the size of the raw data in advance if you want to read it again later.
stream_read_hex(id, bytes) - Reads hexadecimal data from the stream.
stream_write_hex(id, string) - Writes hexadecimal data to the stream.
stream_write_stream(id, source_id) - Reads all available data of the stream source_id and writes it to the stream id. The source and destination stream can be the same.
stream_write_stream_part(id, source_id, bytes) - Reads up to bytes bytes of data of the stream source_id and writes it to the stream id. The source and destination stream can be the same.
stream_get_bytes_left(id) - Returns the number of bytes that are available for reading for this stream.
Buffers are data structures that store binary data. Buffers have a certain length, a reading position and a writing position. Buffers can be resized, and the reading position and writing position can be changed. All buffers are also streams: any stream function will also work for buffers. Again, there are multiple types of buffers, and other DLLs can add new types.
When using a buffer as a stream, data will be read from the reading position, and the reading position will be advanced. New data will be written to the writing position, and the writing position will be advanced. If you try to write past the end of the buffer, the buffer will be enlarged automatically.
buffer_exists(id) - Returns whether the buffer with the given id exists.
buffer_get_read_pos(id) - Returns the current reading position of the buffer.
buffer_get_write_pos(id) - Returns the current writing position of the buffer.
buffer_get_length(id) - Returns the length of the buffer.
buffer_set_read_pos(id, readpos) - Changes the current reading position of the buffer.
buffer_set_write_pos(id, writepos) - Changes the current writing position of the buffer.
buffer_set_length(id, length) - Changes the length of the buffer.
Memory buffers are buffers that store their data in RAM memory. They are very fast and useful to store data temporarily.
memorybuffer_create(id) - Returns the current reading position of the buffer.
memorybuffer_destroy(id) - Returns the current writing position of the buffer.
memorybuffer_exists(id) - Returns whether the memory buffer with the given id exists.
memorybuffer_read_from_file(id, filename) - Clears the memory buffer, then reads the contents of the file with the given filename, and copies the contents to the memory buffer. Returns whether successful.
memorybuffer_write_to_file(id, filename) - Writes the contents of the memory buffer to the file with the given filename. The file will be created if it doesn't exist, or overwritten if it does exist. Returns whether successful.
shared_stream_buffers_get_handle() - This function returns the address of the Shared Stream Buffer Interface. You can't use this number in GML, but other DLLs will need it to use Shared Stream Buffers.
Writing data to a memory buffer and storing it in a file:
var buffer; // create a memory buffer buffer = memorybuffer_create(); // write data to the memory buffer stream_write_uint8(buffer, 42); stream_write_uint8(buffer, 77); stream_write_float32(buffer, 3.14); stream_write_string(buffer, "hello world"); // save the data to a file memorybuffer_write_to_file(buffer, "data.txt"); // destroy the memory buffer memorybuffer_destroy(buffer);
Loading the file and reading the data:
var buffer, a, b, c, d; // create a memory buffer buffer = memorybuffer_create(); // read the data from the file if !memorybuffer_read_from_file(buffer, "data.txt") { memorybuffer_destroy(buffer); show_message("Error: Reading data.txt failed! Make sure the file exists."); exit; } // read data from the buffer // this should be done in exactly the same order a = stream_read_uint8(buffer); b = stream_uint8(buffer); c = stream_read_float32(buffer); d = stream_read_string(buffer); // display the result show_message("a = "+string(a)+"#b = "+string(b)+"#c = "+string(c)+"#d = "+d); // output: // a = 42 // b = 77 // c = 3.14 // d = hello world // destroy the memory buffer memorybuffer_destroy(buffer);
If you're creating your own DLL and you want to use Shared Stream Buffers, just include SharedStreamBuffers.h in your project. This file defines a number of classes which your DLL can use as an interface to manipulate buffers. But before you can use the SharedStreamBufferInterface, you have to create a function to set the SharedStreamBufferInterface address:
#include "SharedBuffers.h" SharedStreamBufferInterface *ssbi = NULL; gmexport double yourdllname_init_shared_stream_buffers(double handle) { ssbi = (SharedStreamBufferInterface*)((uintptr_t)(handle)); return 1; }
This function has to be called in Game Maker before you can use the Shared Stream Buffers:
// initialize shared buffers interface for yourdllname.dll yourdllname_init_shared_stream_buffers(shared_stream_buffers_get_handle());
After the interface address has been set, you can use any buffer or stream in your DLL:
gmexport double yourdllname_some_function(double buffer_id) { if(ssbi == NULL) return 0; // the interface address has not been set yet SharedStream *stream = ssbi->FindStream(gm_double_to_uint(id)); if(stream == NULL) return 0; int32_t val1 = 12345; char val2[] = "Hello world"; // null-terminated string float val3 = 42.42f; stream->WriteData(&val1, sizeof(val1)); stream->WriteData(&val2, sizeof(val2)); stream->WriteData(&val3, sizeof(val3)); return 1; }
Under construction :).
Good luck!