Saving Data
WASM-4 supports saving up to 1024 raw bytes of data to persist between sessions.
For the web runtime, the disk is saved into the browser's localStorage and tied to the domain. The localStorage key is chosen when bundling to be unique and not conflict with other games served from the same domain.
For the native runtime, the disk is saved as a file in the same directory as the game.
In the case that a game is updated and its disk layout was changed, it's up to the developer to handle it. A simple method would be to prefix the disk data with a version number and when it doesn't match, either reset the current disk or migrate it to the new format.
#
Writing Data to DiskTo save, use diskw()
.
It takes a source data pointer along with a byte length.
For example, to write a 32 bit integer with the value 1337
to disk:
// First we need to store the value somewhere in memory to get a pointerconst ptr = memory.data(sizeof<i32>());store<i32>(ptr, 1337);
w4.diskw(ptr, sizeof<i32>());
int gameData = 1337;diskw(&gameData, sizeof(gameData));
int game_data = 1337;w4::diskw(&game_data, $sizeof(game_data));
int gameData = 1337;w4.diskw(&gameData, gameData.sizeof);
import "unsafe"// ...
var gameData int32 = 1337w4.DiskW(unsafe.Pointer(&gameData), unsafe.Sizeof(gameData))
local game_data: integer = 1337diskw(&game_data, #@decltype(game_data))
var gameData = 1337'i32discard diskw(addr gameData, uint32 sizeof(gameData))
game_data : i32 = 1337w4.diskw(&game_data, size_of(game_data))
var game_data: i32 = 1337;// Store the gamedata as little-endian bytes.var bits = game_data as u32;var buffer: [4]u8 = [ ((bits >> 24) & 0xFF) as u8, ((bits >> 16) & 0xFF) as u8, ((bits >> 8) & 0xFF) as u8, (bits & 0xFF) as u8,];// Write the bytes to disk.diskw(buffer, |buffer|);
const data "\\39\\05\\00\\00"c end
4 data diskw
let game_data: i32 = 1337;diskw(&game_data transmute &u8, sizeof$i32());
let game_data: i32 = 1337;
unsafe { let game_data_bytes = game_data.to_le_bytes(); diskw(game_data_bytes.as_ptr(), core::mem::size_of::<i32>() as u32);}
;; game data (1337 stored in little-endian format)(data (i32.const 0x2000) "\39\05\00\00")
;; write 4 bytes from address 0x2000(call $diskw (i32.const 0x2000) (i32.const 4))
var game_data: i32 = 1337;_ = w4.diskw(@ptrCast([*]u8, &game_data), @sizeOf(@TypeOf(game_data)));
#
Reading Data from DiskReading is similar, using diskr()
. It takes a destination pointer along with
a byte length.
For example, to read a 32 bit integer from disk:
const ptr = memory.data(sizeof<i32>());w4.diskr(ptr, sizeof<i32>());
const gameData = load<i32>(ptr);
int gameData;diskr(&gameData, sizeof(gameData));
int game_data;w4::diskr(&game_data, $sizeof(game_data));
int gameData;w4.diskr(&gameData, gameData.sizeof);
import "unsafe"// ...
var gameData int32w4.DiskR(unsafe.Pointer(&gameData), unsafe.Sizeof(gameData))
local game_data: integerdiskr(&game_data, #@decltype(game_data))
var gameData: int32discard diskr(addr gameData, uint32 sizeof(gameData))
game_data : i32w4.diskr(&game_data, size_of(game_data))
var game_data: i32;// Read little-endian bytes from disk.var buffer: [4]u8 = [0, 0, 0, 0];diskr(buffer, |buffer|);var bits = (buffer[0] as u32 << 24) | (buffer[1] as u32 << 16) | (buffer[2] as u32 << 8) | (buffer[3] as u32);game_data = bits as i32;
memory game_data 4 end
4 game_data diskr
let game_data: i32 = 0;diskr(&game_data transmute &u8, sizeof$i32());
let game_data = unsafe { let mut buffer = [0u8; core::mem::size_of::<i32>()];
diskr(buffer.as_mut_ptr(), buffer.len() as u32);
i32::from_le_bytes(buffer)};
;; Read 4 bytes into memory at address 0x2000.(call $diskr (i32.const 0x2000) (i32.const 4))
var game_data: i32 = undefined;_ = w4.diskr(@ptrCast([*]u8, &game_data), @sizeOf(@TypeOf(game_data)));