Ibtool replacement

There are a few packages which seem to require ibtool. This binary comes from the full Xcode rather than cli tools for some reason, and that would make the package non-free.

In a moment of madness I thought - what’s to stop me from reimplementing it? Turns out there’s a project on GH already which can process some iOS xib files, but it’s extremely alpha and has bugs. But after a few changes, I can process the smallest of Transmission’s xib files. (With lots of attributes still missing, but still)

But before I dig too deep and polish the results to be 100% matching:

  1. Has anyone tried to do that yet? Are there any known “this will not work because…” reasons?
  2. Has anyone thought about it and collected any minimal .xib files? Just getting enough good samples is an annoying job.

Pinging some Darwin people just in case :wink: @reckenrode @emily

3 Likes

I tried it once for the Vulkan-Tools cube demo, but it didn’t work. That may be one simple example. Another could be the Mac pinentry for GNUPG. There are translated nibs checked into nixpkgs, which might be useful as references for intended output. They’re not minimal, but the apps are not complicated.

Edit: Added pinentry links. The cube demo is using storyboards, which may not be what you wantz

I don’t think there’s any fundamental reason it couldn’t be done, just… nobody has wanted to do it :slight_smile: If you got somewhere with it, that would be incredible!

Although I warn you that your next job would be to write a Metal shader compiler that targets an undocumented IR… (and xcbuild really needs a maintainer or a rewrite…)

your next job would be to write a Metal shader compiler

I’m up for some fun RE, not for a hard labor punishment :stuck_out_tongue:

Just in case someone looks for this in the future, search keywords: imhex nib pattern reverse engineering

import std.mem;
import std.io;
import std.core;
import std.string;

using FlexNumber;

fn flex_value(ref FlexNumber num) {
    u32 res = 0;
    for (u8 i=0, i < sizeof(num.values), i=i+1){
        res = res + ((num.values[i] & 0x7f) << (i*7));
    }
    return res;
};

struct FlexNumber {
    u32 _location = $;
    u8 values[while($ == _location || std::mem::read_unsigned($-1, 1) & 0x80 == 0)];
} [[format_read("flex_value"),transform("flex_value")]];

struct Class {
    FlexNumber length;
    u8 tp;
    if(tp == 0x81) {
        u32 unknown;
    }
    char name[length];
};

fn class_mapping(ref FlexNumber idx) {
    return classes[idx].name;
};

struct ObjectEntry {
    FlexNumber class_idx [[format_read("class_mapping")]];
    FlexNumber props_first_idx;
    FlexNumber num_props;
} [[single_color]];

struct Label {
    FlexNumber length [[hidden]];
    char value[length];
} [[single_color,inline]];

enum Encoding : u8 {
    Byte = 0,
    Short = 1,
    Word = 2,
    DWord = 3,
    False = 4,
    True = 5,
    Float = 6,
    Double = 7,
    String = 8,
    Nil = 9,
    Object = 10,
};

fn label_mapping(ref FlexNumber idx) {
    return labels[idx].value;
};

fn object_ref(u32 id) {
    return std::format("@{}", id);
};

using Property;

fn prop_string(ref Property prop) {
    str label = labels[prop.label_idx].value;
    str type = std::string::substr(std::core::formatted_value(prop.encoding), 10, 20);
    if(std::core::has_member(prop, "value")) {
        return std::format("{}/{} ({})", label, prop.value, type);
    } else {
        return std::format("{}/{}", label, type);
    }
};

struct Property {
    FlexNumber label_idx [[format_read("label_mapping")]];
    Encoding encoding;
    match(encoding) {
        (Encoding::Byte): u8 value;
        (Encoding::Short): u16 value;
        (Encoding::Word): u32 value;
        (Encoding::DWord): u64 value;
        (Encoding::Float): float value;
        (Encoding::Double): double value;
        (Encoding::String): {
            FlexNumber length;
            char value[length];
        }
        (Encoding::Object): u32 value [[format_read("object_ref")]];
    }
} [[single_color,format_read("prop_string")]];

struct Section {
    u32 obj_count;
    u32 address;
};

struct Header {
    u32 size;
    u32 section_count = (size-1)/2;
    Section sections[section_count];
};

char magic[10]@0;
u32 unknown@10;
Header header@14;

Section cls_section = header.sections[3];
Class classes[cls_section.obj_count] @ cls_section.address;

Section objs_section = header.sections[0];
ObjectEntry objs[objs_section.obj_count] @ objs_section.address;

Section labels_section = header.sections[1];
Label labels[labels_section.obj_count] @ labels_section.address;

Section props_section = header.sections[2];
Property props[props_section.obj_count] @ props_section.address;

The project is at GitHub - viraptor/ibtool: iOS Interface Builder utility, implemented in python.
I had to break it a bit to remove bad assumptions, but the minimal sample is 1:1 compatible with Apple’s output. I’ll keep adding small bits to it until Transmission can be compiled this way.

1 Like