untitled-roguelike/source/utils_web.odin
2026-05-03 02:09:13 -07:00

109 lines
No EOL
2.9 KiB
Odin

// Implementations of `read_entire_file` and `write_entire_file` using the libc
// stuff emscripten exposes. You can read the files that get bundled by
// `--preload-file assets` in `build_web` script.
#+build wasm32, wasm64p32
package game
import "base:runtime"
import "core:log"
import "core:c"
import "core:strings"
// These will be linked in by emscripten.
@(default_calling_convention = "c")
foreign {
fopen :: proc(filename, mode: cstring) -> ^FILE ---
fseek :: proc(stream: ^FILE, offset: c.long, whence: Whence) -> c.int ---
ftell :: proc(stream: ^FILE) -> c.long ---
fclose :: proc(stream: ^FILE) -> c.int ---
fread :: proc(ptr: rawptr, size: c.size_t, nmemb: c.size_t, stream: ^FILE) -> c.size_t ---
fwrite :: proc(ptr: rawptr, size: c.size_t, nmemb: c.size_t, stream: ^FILE) -> c.size_t ---
}
@(private="file")
FILE :: struct {}
Whence :: enum c.int {
SET,
CUR,
END,
}
// Similar to raylib's LoadFileData
_read_entire_file :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
if name == "" {
log.error("No file name provided")
return
}
file := fopen(strings.clone_to_cstring(name, context.temp_allocator), "rb")
if file == nil {
log.errorf("Failed to open file %v", name)
return
}
defer fclose(file)
fseek(file, 0, .END)
size := ftell(file)
fseek(file, 0, .SET)
if size <= 0 {
log.errorf("Failed to read file %v", name)
return
}
data_err: runtime.Allocator_Error
data, data_err = make([]byte, size, allocator, loc)
if data_err != nil {
log.errorf("Error allocating memory: %v", data_err)
return
}
read_size := fread(raw_data(data), 1, c.size_t(size), file)
if read_size != c.size_t(size) {
log.warnf("File %v partially loaded (%i bytes out of %i)", name, read_size, size)
}
log.debugf("Successfully loaded %v", name)
return data, true
}
// Similar to raylib's SaveFileData.
//
// Note: This can save during the current session, but I don't think you can
// save any data between sessions. So when you close the tab your saved files
// are gone. Perhaps you could communicate back to emscripten and save a cookie.
// Or communicate with a server and tell it to save data.
_write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (success: bool) {
if name == "" {
log.error("No file name provided")
return
}
file := fopen(strings.clone_to_cstring(name, context.temp_allocator), truncate ? "wb" : "ab")
defer fclose(file)
if file == nil {
log.errorf("Failed to open '%v' for writing", name)
return
}
bytes_written := fwrite(raw_data(data), 1, len(data), file)
if bytes_written == 0 {
log.errorf("Failed to write file %v", name)
return
} else if bytes_written != len(data) {
log.errorf("File partially written, wrote %v out of %v bytes", bytes_written, len(data))
return
}
log.debugf("File written successfully: %v", name)
return true
}