Skip to content

add ltk_inibin: INIBIN v1/v2 parser and writer with bucket API#121

Open
FrogCsLoL wants to merge 3 commits intoLeagueToolkit:mainfrom
FrogCsLoL:add-ltk-troybin
Open

add ltk_inibin: INIBIN v1/v2 parser and writer with bucket API#121
FrogCsLoL wants to merge 3 commits intoLeagueToolkit:mainfrom
FrogCsLoL:add-ltk-troybin

Conversation

@FrogCsLoL
Copy link
Contributor

Summary

  • Adds ltk_inibin crate for parsing and writing inibin files (.inibin, .troybin, .cfgbin — identical format)
  • Bucket representation matching C# reference: InibinFile → InibinSet → InibinValue
  • Public API: get/get_from/contains/add_value/remove/iter by hash key
  • Reader supports v1 + v2, writer outputs v2
  • No hash resolution baked in — that's a separate concern

Changes from previous PR (#120)

Reworked per review feedback:

  • Renamed ltk_troybin → ltk_inibin
  • Replaced flat Vec with typed bucket API (IndexMap<InibinFlags, InibinSet>)
  • Granular InibinValue enum matching ltk_meta conventions
  • Removed dictionary/hash/text modules (hash resolution is not core)
  • Follows ltk collection API patterns (get/get_mut/contains/insert/remove/len/is_empty/iter)

Adds ltk_inibin crate for parsing and writing inibin files (.inibin,
.troybin, .cfgbin — all the same format). Modeled after the C# reference
implementation with a bucket representation:

- InibinFile: IndexMap<InibinFlags, InibinSet> — sets grouped by type
- InibinSet: IndexMap<u32, InibinValue> — hash-keyed typed bucket
- InibinValue: granular typed enum (I32, F32, Bool, String, Vec2/3/4, etc)
- Public API: get/get_from/contains/add_value/remove/iter
- Reader: v1 + v2 binary support
- Writer: v2 binary serialization
- No hash resolution — that is a separate concern

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@FrogCsLoL FrogCsLoL requested a review from alanpq March 25, 2026 23:32
UnknownVersion(u8),

#[error("Cannot write v1 (old format) entries to binary — convert to v2 storage types first")]
V1WriteNotSupported,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an obsolete error as we don't really care about v1 writing. We should write "v2" by default.

}

/// Write an [`InibinFile`] to binary v2 format.
pub fn write<W: std::io::Write>(w: &mut W, file: &InibinFile) -> Result<(), InibinError> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function should be implemented directly on top of an Inibin struct.
Additionally it would be good to rename InibinFile to just Inibin for brewity.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All structs should have the Inibin prefix removed, besides the actual Inibin struct

#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct InibinFile {
version: u8,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't really care about the version of the source file when representing it in memory.

@Crauzer
Copy link
Member

Crauzer commented Mar 26, 2026

Can you please remove Claude as a contributor in the git commits for the PR

@Crauzer
Copy link
Member

Crauzer commented Mar 26, 2026

I'll work on some changes to this branch as well so we can merge it earlier.
Particularly I'd handle the public API so we document how it all works, the tricky part will be getting the string fixes to work statically in a good way. We can potentially include them in a separate crate that would be baked into code statically.

@Crauzer
Copy link
Member

Crauzer commented Mar 26, 2026

There is also gonna be an issue of how to represent the values in a sane way from a public API. The API we can provide will be something similar to the current meta/ritobin crates more or less.

@Crauzer
Copy link
Member

Crauzer commented Mar 26, 2026

We also need to add the SDBM hash into the existing ltk_hash crate


// ── V1 reader ───────────────────────────────────────────────────────────

fn sanitize_str(s: &str) -> InibinValue {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should probably just be a FromStr impl on InibinValue


let mut properties = IndexMap::with_capacity(num);
for &key in &keys {
let value = match flags {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this be extracted to a method like from_reader on InibinValue?


fn read_strings<R: Read>(r: &mut R, strings_length: usize) -> Result<InibinSet, InibinError> {
let num = r.read_u16::<LE>()? as usize;
let mut keys = Vec::with_capacity(num);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you change these to iterators as well

}

/// Read an inibin binary from a byte slice.
pub fn from_slice(data: &[u8]) -> Result<InibinFile, InibinError> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be a from_reader method on InibinFile, with an additional from_slice helper method

}

/// Get a reference to a set by type.
pub fn set(&self, flags: InibinFlags) -> Option<&InibinSet> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Crauzer thoughts on renaming Sets to Sections, set makes methods like these confusing

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that seems like a good idea

///
/// Sets with [`InibinFlags::OldFormat`] are skipped — v1 is read-only.
/// Returns [`InibinError::V1WriteNotSupported`] if all sets are old format.
pub fn write<W: Write>(w: &mut W, file: &InibinFile) -> Result<(), InibinError> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be method on Inibin

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)]
pub enum InibinFlags {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems like it should be named ValueKind instead, Flags implies bitflags

FrogCsLoL and others added 2 commits March 26, 2026 12:34
Co-authored-by: Alan Panayotov <alanpanayotov@gmail.com>
Co-authored-by: Alan Panayotov <alanpanayotov@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants