What is deterministic is made at build time by the game's own code (`ludic bake`) and read at run time. baked.ludic, baked_hash.ludic, baked_stream.ludic: - The header, 32 bytes little-endian: "LBAK" | u32 format (BAKE_FORMAT 1) | u32 generator version | u32 payload offset | u64 inputs hash | u64 payload length | then the key (UTF-8, zero-padded to 8), then the payload at its offset, whose layout is the bake's own. - bake_write(path, key, version, inputs_hash, payload, n) makes the directories and writes it; bake_inputs_hash(inputs) is FNV-1a 64 over each space-separated input: its path, a 0, its bytes. - baked_open(path, key, version) -> []byte: the payload as a view of the file (no copy), or null for a missing file or another format, key or version. The runtime never hashes inputs; `ludic bake --check` holds a baked file to them at build time. - baked_open_range(path, key, version) -> BakedFile kept open (header checked once), baked_read(bf, at, n, into) -> count read into the caller's buffer from payload offset `at` (held to the buffer and the payload), baked_len, baked_close: a streamed bake (placements by chunk, terrain by tile) with nothing made per read. Pack-aware through file_open: on macOS a packed entry is an fmemopen over the mapped pack (a seek is free), on Windows a temporary copy of the entry made once when it is opened. - baked_path(map, file), bake_missing(bake_st, key) (a dev build's line, once a key). tests/baked_test.ludic: a payload round-trips for its key and version only, a streamed read at an offset and one past the end, the inputs hash follows path and bytes. Written, type-checked, not run (compile-only rule). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
47 lines
1.8 KiB
Text
47 lines
1.8 KiB
Text
# baked_test.ludic - a baked file round-trips its payload; another version or key reads as null; the
|
|
# inputs hash is FNV-1a 64 over path, 0 and bytes (tools/bake/baked.py computes the same)
|
|
import "ludic.base"
|
|
program BakedTest {
|
|
function dir() -> string { return Os.temp_dir() + "/ludic-baked-test" }
|
|
test "a baked file's payload comes back, for its key and version only" {
|
|
let p = buffer(5)
|
|
for i in 0 .. 5 { p[i] = 65 + i }
|
|
let path = dir() + "/a/b.bin"
|
|
expect(bake_write(path, "tiles", 3, 42, p, 5))
|
|
let b = baked_open(path, "tiles", 3)
|
|
expect(b != null)
|
|
expect_eq(len(b), 5)
|
|
expect_eq(b[4], 69)
|
|
expect(baked_open(path, "tiles", 4) == null)
|
|
expect(baked_open(path, "tile", 3) == null)
|
|
expect(baked_open(dir() + "/none.bin", "tiles", 3) == null)
|
|
}
|
|
test "a streamed bake reads a piece of its payload at an offset into the caller's buffer" {
|
|
let p = buffer(10)
|
|
for i in 0 .. 10 { p[i] = 48 + i }
|
|
let path = dir() + "/s.bin"
|
|
expect(bake_write(path, "chunks", 1, 0, p, 10))
|
|
let bf = baked_open_range(path, "chunks", 1)
|
|
expect(bf != null)
|
|
expect(baked_len(bf) == 10)
|
|
let into = buffer(4)
|
|
expect_eq(baked_read(bf, 6, 4, into), 4)
|
|
expect_eq(into[0], 54)
|
|
expect_eq(baked_read(bf, 8, 4, into), 2)
|
|
baked_close(bf)
|
|
expect(baked_open_range(path, "chunks", 2) == null)
|
|
}
|
|
|
|
test "the inputs hash follows the path and the bytes" {
|
|
Fs.write_text(dir() + "/in.txt", "abc")
|
|
let h1 = bake_inputs_hash(dir() + "/in.txt")
|
|
Fs.write_text(dir() + "/in.txt", "abd")
|
|
expect(bake_inputs_hash(dir() + "/in.txt") != h1)
|
|
expect(bake_inputs_hash("") == -3750763034362895579)
|
|
}
|
|
test "a dev build says a missing bake once" (bake_st: mut BakeState) {
|
|
bake_missing(bake_st, "k")
|
|
bake_missing(bake_st, "k")
|
|
expect_eq(len(bake_st.said), 1)
|
|
}
|
|
}
|