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 Ok(GlobalVarDef {
309 id,
310 name,
311 value_type,
312 default_value,
313 mutable,
314 })
315}
316
317fn decode_value_type(buf: &[u8], off: &mut usize) -> Result<ValueType, DecodeError> {
318 let tag = read_u8(buf, off)?;
319 match tag {
320 VAL_INT => Ok(ValueType::Int),
321 VAL_FLOAT => Ok(ValueType::Float),
322 VAL_BOOL => Ok(ValueType::Bool),
323 VAL_STRING => Ok(ValueType::String),
324 VAL_LIST => Ok(ValueType::List),
325 VAL_DIVERT_TARGET => Ok(ValueType::DivertTarget),
326 VAL_VAR_POINTER => Ok(ValueType::VariablePointer),
327 VAL_FRAGMENT_REF => Ok(ValueType::FragmentRef),
328 VAL_NULL => Ok(ValueType::Null),
329 _ => Err(DecodeError::InvalidValueType(tag)),
330 }
331}
332
333fn decode_value(buf: &[u8], off: &mut usize) -> Result<Value, DecodeError> {
334 let tag = read_u8(buf, off)?;
335 match tag {
336 VAL_INT => Ok(Value::Int(read_i32(buf, off)?)),
337 VAL_FLOAT => {
338 if *off + 4 > buf.len() {
339 return Err(DecodeError::UnexpectedEof);
340 }
341 let v = f32::from_le_bytes([buf[*off], buf[*off + 1], buf[*off + 2], buf[*off + 3]]);
342 *off += 4;
343 Ok(Value::Float(v))
344 }
345 VAL_BOOL => Ok(Value::Bool(read_u8(buf, off)? != 0)),
346 VAL_STRING => Ok(Value::String(read_str(buf, off)?.into())),
347 VAL_LIST => {
348 let item_count = read_u32(buf, off)? as usize;
349 let mut items = Vec::with_capacity(safe_capacity(item_count, buf.len(), *off, 8));
350 for _ in 0..item_count {
351 items.push(read_def_id(buf, off)?);
352 }
353 let origin_count = read_u32(buf, off)? as usize;
354 let mut origins = Vec::with_capacity(safe_capacity(origin_count, buf.len(), *off, 8));
355 for _ in 0..origin_count {
356 origins.push(read_def_id(buf, off)?);
357 }
358 Ok(Value::List(ListValue { items, origins }.into()))
359 }
360 VAL_DIVERT_TARGET => Ok(Value::DivertTarget(read_def_id(buf, off)?)),
361 VAL_VAR_POINTER => Ok(Value::VariablePointer(read_def_id(buf, off)?)),
362 VAL_FRAGMENT_REF => Ok(Value::FragmentRef(read_u32(buf, off)?)),
363 VAL_NULL => Ok(Value::Null),
364 _ => Err(DecodeError::InvalidValueType(tag)),
365 }
366}
367
368fn decode_list_def(buf: &[u8], off: &mut usize) -> Result<ListDef, DecodeError> {
369 let id = read_def_id(buf, off)?;
370 let name = NameId(read_u16(buf, off)?);
371 let item_count = read_u32(buf, off)? as usize;
372 let mut items = Vec::with_capacity(safe_capacity(item_count, buf.len(), *off, 6));
373 for _ in 0..item_count {
374 let name_id = NameId(read_u16(buf, off)?);
375 let ordinal = read_i32(buf, off)?;
376 items.push((name_id, ordinal));
377 }
378 Ok(ListDef { id, name, items })
379}
380
381fn decode_list_item(buf: &[u8], off: &mut usize) -> Result<ListItemDef, DecodeError> {
382 let id = read_def_id(buf, off)?;
383 let origin = read_def_id(buf, off)?;
384 let ordinal = read_i32(buf, off)?;
385 let name = NameId(read_u16(buf, off)?);
386 Ok(ListItemDef {
387 id,
388 origin,
389 ordinal,
390 name,
391 })
392}
393
394pub fn read_section_list_literals(
396 buf: &[u8],
397 index: &InkbIndex,
398) -> Result<Vec<ListValue>, DecodeError> {
399 let Some(range) = index.section_range(SectionKind::ListLiterals) else {
400 return Ok(Vec::new());
401 };
402 let mut off = range.start;
403 let count = read_u32(buf, &mut off)? as usize;
404 let mut literals = Vec::with_capacity(safe_capacity(count, buf.len(), off, 8));
405 for _ in 0..count {
406 let item_count = read_u32(buf, &mut off)? as usize;
407 let mut items = Vec::with_capacity(safe_capacity(item_count, buf.len(), off, 8));
408 for _ in 0..item_count {
409 items.push(read_def_id(buf, &mut off)?);
410 }
411 let origin_count = read_u32(buf, &mut off)? as usize;
412 let mut origins = Vec::with_capacity(safe_capacity(origin_count, buf.len(), off, 8));
413 for _ in 0..origin_count {
414 origins.push(read_def_id(buf, &mut off)?);
415 }
416 literals.push(ListValue { items, origins });
417 }
418 Ok(literals)
419}
420
421fn decode_external(buf: &[u8], off: &mut usize) -> Result<ExternalFnDef, DecodeError> {
422 let id = read_def_id(buf, off)?;
423 let name = NameId(read_u16(buf, off)?);
424 let arg_count = read_u8(buf, off)?;
425 let has_fallback = read_u8(buf, off)? != 0;
426 let fallback = if has_fallback {
427 Some(read_def_id(buf, off)?)
428 } else {
429 None
430 };
431 Ok(ExternalFnDef {
432 id,
433 name,
434 arg_count,
435 fallback,
436 })
437}
438
439fn decode_container(buf: &[u8], off: &mut usize) -> Result<ContainerDef, DecodeError> {
440 let id = read_def_id(buf, off)?;
441 let scope_id = read_def_id(buf, off)?;
442 let has_name = read_u8(buf, off)? != 0;
443 let name = if has_name {
444 Some(NameId(read_u16(buf, off)?))
445 } else {
446 None
447 };
448 let counting_bits = read_u8(buf, off)?;
449 let counting_flags = CountingFlags::from_bits(counting_bits).unwrap_or(CountingFlags::empty());
450 let path_hash = read_i32(buf, off)?;
451 let param_count = read_u8(buf, off)?;
452
453 let bytecode_len = read_u32(buf, off)? as usize;
454 if *off + bytecode_len > buf.len() {
455 return Err(DecodeError::UnexpectedEof);
456 }
457 let bytecode = buf[*off..*off + bytecode_len].to_vec();
458 *off += bytecode_len;
459
460 Ok(ContainerDef {
461 id,
462 scope_id,
463 name,
464 bytecode,
465 counting_flags,
466 path_hash,
467 param_count,
468 })
469}
470
471pub fn read_section_line_tables(
473 buf: &[u8],
474 index: &InkbIndex,
475) -> Result<Vec<ScopeLineTable>, DecodeError> {
476 let range =
477 index
478 .section_range(SectionKind::LineTables)
479 .ok_or(DecodeError::MissingSectionKind(
480 SectionKind::LineTables as u8,
481 ))?;
482 let mut off = range.start;
483 let count = read_u32(buf, &mut off)? as usize;
484 let mut tables = Vec::with_capacity(safe_capacity(count, buf.len(), off, 12));
485 for _ in 0..count {
486 tables.push(decode_scope_line_table(buf, &mut off)?);
487 }
488 Ok(tables)
489}
490
491fn decode_scope_line_table(buf: &[u8], off: &mut usize) -> Result<ScopeLineTable, DecodeError> {
492 let scope_id = read_def_id(buf, off)?;
493 let line_count = read_u32(buf, off)? as usize;
494 let mut lines = Vec::with_capacity(safe_capacity(line_count, buf.len(), *off, 9));
495 for _ in 0..line_count {
496 lines.push(decode_line_entry(buf, off)?);
497 }
498 Ok(ScopeLineTable { scope_id, lines })
499}
500
501fn decode_line_entry(buf: &[u8], off: &mut usize) -> Result<LineEntry, DecodeError> {
502 let content = decode_line_content(buf, off)?;
503 let source_hash = read_u64(buf, off)?;
504 let has_audio = read_u8(buf, off)? != 0;
505 let audio_ref = if has_audio {
506 Some(read_str(buf, off)?)
507 } else {
508 None
509 };
510 let slot_count = read_u8(buf, off)? as usize;
512 let mut slot_info = Vec::with_capacity(slot_count);
513 for _ in 0..slot_count {
514 let index = read_u8(buf, off)?;
515 let name = read_str(buf, off)?;
516 slot_info.push(SlotInfo { index, name });
517 }
518
519 let has_source_loc = read_u8(buf, off)? != 0;
521 let source_location = if has_source_loc {
522 let file = read_str(buf, off)?;
523 let range_start = read_u32(buf, off)?;
524 let range_end = read_u32(buf, off)?;
525 Some(SourceLocation {
526 file,
527 range_start,
528 range_end,
529 })
530 } else {
531 None
532 };
533
534 let flags = crate::LineFlags::from_content(&content);
535 Ok(LineEntry {
536 content,
537 flags,
538 source_hash,
539 audio_ref,
540 slot_info,
541 source_location,
542 })
543}
544
545pub(crate) fn decode_line_content(buf: &[u8], off: &mut usize) -> Result<LineContent, DecodeError> {
546 let tag = read_u8(buf, off)?;
547 match tag {
548 LINE_PLAIN => Ok(LineContent::Plain(read_str(buf, off)?)),
549 LINE_TEMPLATE => {
550 let part_count = read_u32(buf, off)? as usize;
551 let mut parts = Vec::with_capacity(safe_capacity(part_count, buf.len(), *off, 2));
552 for _ in 0..part_count {
553 parts.push(decode_line_part(buf, off)?);
554 }
555 Ok(LineContent::Template(parts))
556 }
557 _ => Err(DecodeError::InvalidLineContent(tag)),
558 }
559}
560
561fn decode_line_part(buf: &[u8], off: &mut usize) -> Result<LinePart, DecodeError> {
562 let tag = read_u8(buf, off)?;
563 match tag {
564 PART_LITERAL => Ok(LinePart::Literal(read_str(buf, off)?)),
565 PART_SLOT => Ok(LinePart::Slot(read_u8(buf, off)?)),
566 PART_SELECT => {
567 let slot = read_u8(buf, off)?;
568 let variant_count = read_u32(buf, off)? as usize;
569 let mut variants = Vec::with_capacity(safe_capacity(variant_count, buf.len(), *off, 6));
570 for _ in 0..variant_count {
571 let key = decode_select_key(buf, off)?;
572 let text = read_str(buf, off)?;
573 variants.push((key, text));
574 }
575 let default = read_str(buf, off)?;
576 Ok(LinePart::Select {
577 slot,
578 variants,
579 default,
580 })
581 }
582 _ => Err(DecodeError::InvalidLinePart(tag)),
583 }
584}
585
586fn decode_select_key(buf: &[u8], off: &mut usize) -> Result<SelectKey, DecodeError> {
587 let tag = read_u8(buf, off)?;
588 match tag {
589 KEY_CARDINAL => Ok(SelectKey::Cardinal(decode_plural_category(buf, off)?)),
590 KEY_ORDINAL => Ok(SelectKey::Ordinal(decode_plural_category(buf, off)?)),
591 KEY_EXACT => Ok(SelectKey::Exact(read_i32(buf, off)?)),
592 KEY_KEYWORD => Ok(SelectKey::Keyword(read_str(buf, off)?)),
593 _ => Err(DecodeError::InvalidSelectKey(tag)),
594 }
595}
596
597fn decode_plural_category(buf: &[u8], off: &mut usize) -> Result<PluralCategory, DecodeError> {
598 let tag = read_u8(buf, off)?;
599 match tag {
600 CAT_ZERO => Ok(PluralCategory::Zero),
601 CAT_ONE => Ok(PluralCategory::One),
602 CAT_TWO => Ok(PluralCategory::Two),
603 CAT_FEW => Ok(PluralCategory::Few),
604 CAT_MANY => Ok(PluralCategory::Many),
605 CAT_OTHER => Ok(PluralCategory::Other),
606 _ => Err(DecodeError::InvalidPluralCategory(tag)),
607 }
608}