1use crate::codec::{crc32, read_def_id, read_i32, read_str, read_u8, read_u16, read_u32, read_u64};
4use crate::counting::CountingFlags;
5use crate::definition::{
6 AddressDef, AddressPath, ContainerDef, ExternalFnDef, GlobalVarDef, LineEntry, ListDef,
7 ListItemDef, ScopeLineTable, SlotInfo, SourceLocation,
8};
9use crate::id::NameId;
10use crate::line::{LineContent, LinePart, PluralCategory, SelectKey};
11use crate::opcode::DecodeError;
12use crate::story::StoryData;
13use crate::value::{ListValue, Value, ValueType};
14
15use super::{
16 CAT_FEW, CAT_MANY, CAT_ONE, CAT_OTHER, CAT_TWO, CAT_ZERO, HEADER_PREAMBLE, InkbIndex,
17 KEY_CARDINAL, KEY_EXACT, KEY_KEYWORD, KEY_ORDINAL, LINE_PLAIN, LINE_TEMPLATE, MAGIC,
18 PART_LITERAL, PART_SELECT, PART_SLOT, SECTION_ENTRY_SIZE, SectionEntry, SectionKind, VAL_BOOL,
19 VAL_DIVERT_TARGET, VAL_FLOAT, VAL_FRAGMENT_REF, VAL_INT, VAL_LIST, VAL_NULL, VAL_STRING,
20 VAL_VAR_POINTER, VERSION, safe_capacity,
21};
22
23pub fn read_inkb(buf: &[u8]) -> Result<StoryData, DecodeError> {
27 let index = read_inkb_index(buf)?;
28
29 let header_size = index.header_size();
31 let computed = crc32(&buf[header_size..]);
32 if computed != index.checksum {
33 return Err(DecodeError::ChecksumMismatch {
34 expected: index.checksum,
35 actual: computed,
36 });
37 }
38
39 let name_table = read_section_name_table(buf, &index)?;
40 let variables = read_section_variables(buf, &index)?;
41 let list_defs = read_section_list_defs(buf, &index)?;
42 let list_items = read_section_list_items(buf, &index)?;
43 let externals = read_section_externals(buf, &index)?;
44 let containers = read_section_containers(buf, &index)?;
45 let line_tables = read_section_line_tables(buf, &index)?;
46 let addresses = read_section_addresses(buf, &index)?;
47 let list_literals = read_section_list_literals(buf, &index)?;
48 let address_paths = read_section_address_paths(buf, &index)?;
49
50 Ok(StoryData {
51 containers,
52 line_tables,
53 variables,
54 list_defs,
55 list_items,
56 externals,
57 addresses,
58 address_paths,
59 name_table,
60 list_literals,
61 source_checksum: index.checksum,
62 })
63}
64
65pub fn read_inkb_index(buf: &[u8]) -> Result<InkbIndex, DecodeError> {
69 if buf.len() < HEADER_PREAMBLE {
70 return Err(DecodeError::UnexpectedEof);
71 }
72
73 let magic: [u8; 4] = [buf[0], buf[1], buf[2], buf[3]];
74 if &magic != MAGIC {
75 return Err(DecodeError::BadMagic(magic));
76 }
77
78 let mut off = 4;
79 let version = read_u16(buf, &mut off)?;
80 if version != VERSION {
81 return Err(DecodeError::UnsupportedVersion(version));
82 }
83
84 let section_count = read_u8(buf, &mut off)?;
85 let _reserved = read_u8(buf, &mut off)?;
86 let file_size = read_u32(buf, &mut off)?;
87 let checksum = read_u32(buf, &mut off)?;
88
89 if file_size as usize != buf.len() {
91 return Err(DecodeError::FileSizeMismatch {
92 expected: file_size,
93 actual: buf.len(),
94 });
95 }
96
97 let total_header = HEADER_PREAMBLE + section_count as usize * SECTION_ENTRY_SIZE;
98 if buf.len() < total_header {
99 return Err(DecodeError::UnexpectedEof);
100 }
101
102 let mut sections = Vec::with_capacity(section_count as usize);
103 for _ in 0..section_count {
104 let kind_tag = read_u8(buf, &mut off)?;
105 let kind = SectionKind::from_u8(kind_tag)?;
106 let _reserved0 = read_u8(buf, &mut off)?;
107 let _reserved1 = read_u8(buf, &mut off)?;
108 let _reserved2 = read_u8(buf, &mut off)?;
109 let offset = read_u32(buf, &mut off)?;
110 sections.push(SectionEntry { kind, offset });
111 }
112
113 #[expect(clippy::cast_possible_truncation)]
119 let header_size = total_header as u32;
120 let mut prev_offset = header_size;
121 for entry in §ions {
122 if entry.offset < header_size || entry.offset > file_size || entry.offset < prev_offset {
123 return Err(DecodeError::InvalidSectionOffset {
124 kind: entry.kind as u8,
125 offset: entry.offset,
126 });
127 }
128 prev_offset = entry.offset;
129 }
130
131 Ok(InkbIndex {
132 version,
133 file_size,
134 checksum,
135 sections,
136 })
137}
138
139pub fn read_section_name_table(buf: &[u8], index: &InkbIndex) -> Result<Vec<String>, DecodeError> {
143 let range =
144 index
145 .section_range(SectionKind::NameTable)
146 .ok_or(DecodeError::MissingSectionKind(
147 SectionKind::NameTable as u8,
148 ))?;
149 let mut off = range.start;
150 let count = read_u32(buf, &mut off)? as usize;
151 let mut names = Vec::with_capacity(safe_capacity(count, buf.len(), off, 4));
152 for _ in 0..count {
153 names.push(read_str(buf, &mut off)?);
154 }
155 Ok(names)
156}
157
158pub fn read_section_variables(
160 buf: &[u8],
161 index: &InkbIndex,
162) -> Result<Vec<GlobalVarDef>, DecodeError> {
163 let range =
164 index
165 .section_range(SectionKind::Variables)
166 .ok_or(DecodeError::MissingSectionKind(
167 SectionKind::Variables as u8,
168 ))?;
169 let mut off = range.start;
170 let count = read_u32(buf, &mut off)? as usize;
171 let mut vars = Vec::with_capacity(safe_capacity(count, buf.len(), off, 12));
172 for _ in 0..count {
173 vars.push(decode_global_var(buf, &mut off)?);
174 }
175 Ok(vars)
176}
177
178pub fn read_section_list_defs(buf: &[u8], index: &InkbIndex) -> Result<Vec<ListDef>, DecodeError> {
180 let range = index
181 .section_range(SectionKind::ListDefs)
182 .ok_or(DecodeError::MissingSectionKind(SectionKind::ListDefs as u8))?;
183 let mut off = range.start;
184 let count = read_u32(buf, &mut off)? as usize;
185 let mut defs = Vec::with_capacity(safe_capacity(count, buf.len(), off, 14));
186 for _ in 0..count {
187 defs.push(decode_list_def(buf, &mut off)?);
188 }
189 Ok(defs)
190}
191
192pub fn read_section_list_items(
194 buf: &[u8],
195 index: &InkbIndex,
196) -> Result<Vec<ListItemDef>, DecodeError> {
197 let range =
198 index
199 .section_range(SectionKind::ListItems)
200 .ok_or(DecodeError::MissingSectionKind(
201 SectionKind::ListItems as u8,
202 ))?;
203 let mut off = range.start;
204 let count = read_u32(buf, &mut off)? as usize;
205 let mut items = Vec::with_capacity(safe_capacity(count, buf.len(), off, 20));
206 for _ in 0..count {
207 items.push(decode_list_item(buf, &mut off)?);
208 }
209 Ok(items)
210}
211
212pub fn read_section_externals(
214 buf: &[u8],
215 index: &InkbIndex,
216) -> Result<Vec<ExternalFnDef>, DecodeError> {
217 let range =
218 index
219 .section_range(SectionKind::Externals)
220 .ok_or(DecodeError::MissingSectionKind(
221 SectionKind::Externals as u8,
222 ))?;
223 let mut off = range.start;
224 let count = read_u32(buf, &mut off)? as usize;
225 let mut exts = Vec::with_capacity(safe_capacity(count, buf.len(), off, 12));
226 for _ in 0..count {
227 exts.push(decode_external(buf, &mut off)?);
228 }
229 Ok(exts)
230}
231
232pub fn read_section_containers(
234 buf: &[u8],
235 index: &InkbIndex,
236) -> Result<Vec<ContainerDef>, DecodeError> {
237 let range =
238 index
239 .section_range(SectionKind::Containers)
240 .ok_or(DecodeError::MissingSectionKind(
241 SectionKind::Containers as u8,
242 ))?;
243 let mut off = range.start;
244 let count = read_u32(buf, &mut off)? as usize;
245 let mut containers = Vec::with_capacity(safe_capacity(count, buf.len(), off, 21));
246 for _ in 0..count {
247 containers.push(decode_container(buf, &mut off)?);
248 }
249 Ok(containers)
250}
251
252pub fn read_section_addresses(
254 buf: &[u8],
255 index: &InkbIndex,
256) -> Result<Vec<AddressDef>, DecodeError> {
257 let Some(range) = index.section_range(SectionKind::Labels) else {
258 return Ok(Vec::new());
260 };
261 let mut off = range.start;
262 let count = read_u32(buf, &mut off)? as usize;
263 let mut addresses = Vec::with_capacity(safe_capacity(count, buf.len(), off, 20));
265 for _ in 0..count {
266 let id = read_def_id(buf, &mut off)?;
267 let container_id = read_def_id(buf, &mut off)?;
268 let byte_offset = read_u32(buf, &mut off)?;
269 addresses.push(AddressDef {
270 id,
271 container_id,
272 byte_offset,
273 });
274 }
275 Ok(addresses)
276}
277
278pub fn read_section_address_paths(
280 buf: &[u8],
281 index: &InkbIndex,
282) -> Result<Vec<AddressPath>, DecodeError> {
283 let Some(range) = index.section_range(SectionKind::AddressPaths) else {
284 return Ok(Vec::new());
287 };
288 let mut off = range.start;
289 let count = read_u32(buf, &mut off)? as usize;
290 let mut paths = Vec::with_capacity(safe_capacity(count, buf.len(), off, 10));
292 for _ in 0..count {
293 let path = NameId(read_u16(buf, &mut off)?);
294 let target = read_def_id(buf, &mut off)?;
295 paths.push(AddressPath { path, target });
296 }
297 Ok(paths)
298}
299
300fn decode_global_var(buf: &[u8], off: &mut usize) -> Result<GlobalVarDef, DecodeError> {
303 let id = read_def_id(buf, off)?;
304 let name = NameId(read_u16(buf, off)?);
305 let value_type = decode_value_type(buf, off)?;
306 let default_value = decode_value(buf, off)?;
307 let mutable = read_u8(buf, off)? != 0;
308 let local = read_u8(buf, off)? != 0;
309 Ok(GlobalVarDef {
310 id,
311 name,
312 value_type,
313 default_value,
314 mutable,
315 local,
316 })
317}
318
319fn decode_value_type(buf: &[u8], off: &mut usize) -> Result<ValueType, DecodeError> {
320 let tag = read_u8(buf, off)?;
321 match tag {
322 VAL_INT => Ok(ValueType::Int),
323 VAL_FLOAT => Ok(ValueType::Float),
324 VAL_BOOL => Ok(ValueType::Bool),
325 VAL_STRING => Ok(ValueType::String),
326 VAL_LIST => Ok(ValueType::List),
327 VAL_DIVERT_TARGET => Ok(ValueType::DivertTarget),
328 VAL_VAR_POINTER => Ok(ValueType::VariablePointer),
329 VAL_FRAGMENT_REF => Ok(ValueType::FragmentRef),
330 VAL_NULL => Ok(ValueType::Null),
331 _ => Err(DecodeError::InvalidValueType(tag)),
332 }
333}
334
335fn decode_value(buf: &[u8], off: &mut usize) -> Result<Value, DecodeError> {
336 let tag = read_u8(buf, off)?;
337 match tag {
338 VAL_INT => Ok(Value::Int(read_i32(buf, off)?)),
339 VAL_FLOAT => {
340 if *off + 4 > buf.len() {
341 return Err(DecodeError::UnexpectedEof);
342 }
343 let v = f32::from_le_bytes([buf[*off], buf[*off + 1], buf[*off + 2], buf[*off + 3]]);
344 *off += 4;
345 Ok(Value::Float(v))
346 }
347 VAL_BOOL => Ok(Value::Bool(read_u8(buf, off)? != 0)),
348 VAL_STRING => Ok(Value::String(read_str(buf, off)?.into())),
349 VAL_LIST => {
350 let item_count = read_u32(buf, off)? as usize;
351 let mut items = Vec::with_capacity(safe_capacity(item_count, buf.len(), *off, 8));
352 for _ in 0..item_count {
353 items.push(read_def_id(buf, off)?);
354 }
355 let origin_count = read_u32(buf, off)? as usize;
356 let mut origins = Vec::with_capacity(safe_capacity(origin_count, buf.len(), *off, 8));
357 for _ in 0..origin_count {
358 origins.push(read_def_id(buf, off)?);
359 }
360 Ok(Value::List(ListValue { items, origins }.into()))
361 }
362 VAL_DIVERT_TARGET => Ok(Value::DivertTarget(read_def_id(buf, off)?)),
363 VAL_VAR_POINTER => Ok(Value::VariablePointer(read_def_id(buf, off)?)),
364 VAL_FRAGMENT_REF => Ok(Value::FragmentRef(read_u32(buf, off)?)),
365 VAL_NULL => Ok(Value::Null),
366 _ => Err(DecodeError::InvalidValueType(tag)),
367 }
368}
369
370fn decode_list_def(buf: &[u8], off: &mut usize) -> Result<ListDef, DecodeError> {
371 let id = read_def_id(buf, off)?;
372 let name = NameId(read_u16(buf, off)?);
373 let item_count = read_u32(buf, off)? as usize;
374 let mut items = Vec::with_capacity(safe_capacity(item_count, buf.len(), *off, 6));
375 for _ in 0..item_count {
376 let name_id = NameId(read_u16(buf, off)?);
377 let ordinal = read_i32(buf, off)?;
378 items.push((name_id, ordinal));
379 }
380 Ok(ListDef { id, name, items })
381}
382
383fn decode_list_item(buf: &[u8], off: &mut usize) -> Result<ListItemDef, DecodeError> {
384 let id = read_def_id(buf, off)?;
385 let origin = read_def_id(buf, off)?;
386 let ordinal = read_i32(buf, off)?;
387 let name = NameId(read_u16(buf, off)?);
388 Ok(ListItemDef {
389 id,
390 origin,
391 ordinal,
392 name,
393 })
394}
395
396pub fn read_section_list_literals(
398 buf: &[u8],
399 index: &InkbIndex,
400) -> Result<Vec<ListValue>, DecodeError> {
401 let Some(range) = index.section_range(SectionKind::ListLiterals) else {
402 return Ok(Vec::new());
403 };
404 let mut off = range.start;
405 let count = read_u32(buf, &mut off)? as usize;
406 let mut literals = Vec::with_capacity(safe_capacity(count, buf.len(), off, 8));
407 for _ in 0..count {
408 let item_count = read_u32(buf, &mut off)? as usize;
409 let mut items = Vec::with_capacity(safe_capacity(item_count, buf.len(), off, 8));
410 for _ in 0..item_count {
411 items.push(read_def_id(buf, &mut off)?);
412 }
413 let origin_count = read_u32(buf, &mut off)? as usize;
414 let mut origins = Vec::with_capacity(safe_capacity(origin_count, buf.len(), off, 8));
415 for _ in 0..origin_count {
416 origins.push(read_def_id(buf, &mut off)?);
417 }
418 literals.push(ListValue { items, origins });
419 }
420 Ok(literals)
421}
422
423fn decode_external(buf: &[u8], off: &mut usize) -> Result<ExternalFnDef, DecodeError> {
424 let id = read_def_id(buf, off)?;
425 let name = NameId(read_u16(buf, off)?);
426 let arg_count = read_u8(buf, off)?;
427 let has_fallback = read_u8(buf, off)? != 0;
428 let fallback = if has_fallback {
429 Some(read_def_id(buf, off)?)
430 } else {
431 None
432 };
433 Ok(ExternalFnDef {
434 id,
435 name,
436 arg_count,
437 fallback,
438 })
439}
440
441fn decode_container(buf: &[u8], off: &mut usize) -> Result<ContainerDef, DecodeError> {
442 let id = read_def_id(buf, off)?;
443 let scope_id = read_def_id(buf, off)?;
444 let has_name = read_u8(buf, off)? != 0;
445 let name = if has_name {
446 Some(NameId(read_u16(buf, off)?))
447 } else {
448 None
449 };
450 let counting_bits = read_u8(buf, off)?;
451 let counting_flags = CountingFlags::from_bits(counting_bits).unwrap_or(CountingFlags::empty());
452 let path_hash = read_i32(buf, off)?;
453 let param_count = read_u8(buf, off)?;
454 let local = read_u8(buf, off)? != 0;
455
456 let bytecode_len = read_u32(buf, off)? as usize;
457 if *off + bytecode_len > buf.len() {
458 return Err(DecodeError::UnexpectedEof);
459 }
460 let bytecode = buf[*off..*off + bytecode_len].to_vec();
461 *off += bytecode_len;
462
463 Ok(ContainerDef {
464 id,
465 scope_id,
466 name,
467 bytecode,
468 counting_flags,
469 path_hash,
470 param_count,
471 local,
472 })
473}
474
475pub fn read_section_line_tables(
477 buf: &[u8],
478 index: &InkbIndex,
479) -> Result<Vec<ScopeLineTable>, DecodeError> {
480 let range =
481 index
482 .section_range(SectionKind::LineTables)
483 .ok_or(DecodeError::MissingSectionKind(
484 SectionKind::LineTables as u8,
485 ))?;
486 let mut off = range.start;
487 let count = read_u32(buf, &mut off)? as usize;
488 let mut tables = Vec::with_capacity(safe_capacity(count, buf.len(), off, 12));
489 for _ in 0..count {
490 tables.push(decode_scope_line_table(buf, &mut off)?);
491 }
492 Ok(tables)
493}
494
495fn decode_scope_line_table(buf: &[u8], off: &mut usize) -> Result<ScopeLineTable, DecodeError> {
496 let scope_id = read_def_id(buf, off)?;
497 let line_count = read_u32(buf, off)? as usize;
498 let mut lines = Vec::with_capacity(safe_capacity(line_count, buf.len(), *off, 9));
499 for _ in 0..line_count {
500 lines.push(decode_line_entry(buf, off)?);
501 }
502 Ok(ScopeLineTable { scope_id, lines })
503}
504
505fn decode_line_entry(buf: &[u8], off: &mut usize) -> Result<LineEntry, DecodeError> {
506 let content = decode_line_content(buf, off)?;
507 let source_hash = read_u64(buf, off)?;
508 let has_audio = read_u8(buf, off)? != 0;
509 let audio_ref = if has_audio {
510 Some(read_str(buf, off)?)
511 } else {
512 None
513 };
514 let slot_count = read_u8(buf, off)? as usize;
516 let mut slot_info = Vec::with_capacity(slot_count);
517 for _ in 0..slot_count {
518 let index = read_u8(buf, off)?;
519 let name = read_str(buf, off)?;
520 slot_info.push(SlotInfo { index, name });
521 }
522
523 let has_source_loc = read_u8(buf, off)? != 0;
525 let source_location = if has_source_loc {
526 let file = read_str(buf, off)?;
527 let range_start = read_u32(buf, off)?;
528 let range_end = read_u32(buf, off)?;
529 Some(SourceLocation {
530 file,
531 range_start,
532 range_end,
533 })
534 } else {
535 None
536 };
537
538 let flags = crate::LineFlags::from_content(&content);
539 Ok(LineEntry {
540 content,
541 flags,
542 source_hash,
543 audio_ref,
544 slot_info,
545 source_location,
546 })
547}
548
549pub(crate) fn decode_line_content(buf: &[u8], off: &mut usize) -> Result<LineContent, DecodeError> {
550 let tag = read_u8(buf, off)?;
551 match tag {
552 LINE_PLAIN => Ok(LineContent::Plain(read_str(buf, off)?)),
553 LINE_TEMPLATE => {
554 let part_count = read_u32(buf, off)? as usize;
555 let mut parts = Vec::with_capacity(safe_capacity(part_count, buf.len(), *off, 2));
556 for _ in 0..part_count {
557 parts.push(decode_line_part(buf, off)?);
558 }
559 Ok(LineContent::Template(parts))
560 }
561 _ => Err(DecodeError::InvalidLineContent(tag)),
562 }
563}
564
565fn decode_line_part(buf: &[u8], off: &mut usize) -> Result<LinePart, DecodeError> {
566 let tag = read_u8(buf, off)?;
567 match tag {
568 PART_LITERAL => Ok(LinePart::Literal(read_str(buf, off)?)),
569 PART_SLOT => Ok(LinePart::Slot(read_u8(buf, off)?)),
570 PART_SELECT => {
571 let slot = read_u8(buf, off)?;
572 let variant_count = read_u32(buf, off)? as usize;
573 let mut variants = Vec::with_capacity(safe_capacity(variant_count, buf.len(), *off, 6));
574 for _ in 0..variant_count {
575 let key = decode_select_key(buf, off)?;
576 let text = read_str(buf, off)?;
577 variants.push((key, text));
578 }
579 let default = read_str(buf, off)?;
580 Ok(LinePart::Select {
581 slot,
582 variants,
583 default,
584 })
585 }
586 _ => Err(DecodeError::InvalidLinePart(tag)),
587 }
588}
589
590fn decode_select_key(buf: &[u8], off: &mut usize) -> Result<SelectKey, DecodeError> {
591 let tag = read_u8(buf, off)?;
592 match tag {
593 KEY_CARDINAL => Ok(SelectKey::Cardinal(decode_plural_category(buf, off)?)),
594 KEY_ORDINAL => Ok(SelectKey::Ordinal(decode_plural_category(buf, off)?)),
595 KEY_EXACT => Ok(SelectKey::Exact(read_i32(buf, off)?)),
596 KEY_KEYWORD => Ok(SelectKey::Keyword(read_str(buf, off)?)),
597 _ => Err(DecodeError::InvalidSelectKey(tag)),
598 }
599}
600
601fn decode_plural_category(buf: &[u8], off: &mut usize) -> Result<PluralCategory, DecodeError> {
602 let tag = read_u8(buf, off)?;
603 match tag {
604 CAT_ZERO => Ok(PluralCategory::Zero),
605 CAT_ONE => Ok(PluralCategory::One),
606 CAT_TWO => Ok(PluralCategory::Two),
607 CAT_FEW => Ok(PluralCategory::Few),
608 CAT_MANY => Ok(PluralCategory::Many),
609 CAT_OTHER => Ok(PluralCategory::Other),
610 _ => Err(DecodeError::InvalidPluralCategory(tag)),
611 }
612}