Habbo B64 Encoding [Rust programming language] Source 

Joined
Feb 19, 2008
Messages
1,070
Reaction score
525
So I was trying to browse 4chan while vibe coding, and somehow this code showed up in my editor (wow was I foreshadowing vibe coding all these years in advance?).

Here is the base64.rs file, fully vibe coded, unlike my previous releases, not for lack of skill, but I was testing Claude's abilities to translate code from C# to Rust... back like 6 months? It does one hell of a job, I'll tell you that much.

Code:
//! Custom Base64 encoding. Bytes are offset by `0x40` (`'@'`).
//!
//! - `encode(i)` writes exactly 2 bytes: the 12-bit value `i & 0xFFF` in two
//!   6-bit chunks, MSB first. The C# reference writes 2 chars regardless of
//!   the input's magnitude, so callers using ints outside `[0, 4096)` will
//!   see the wraparound — we preserve that.
//! - `decode(s)` reads any-length input as a base-64 (offset 64) big-endian
//!   integer, mirroring the C# loop. Empty input decodes to 0.
//! - `get_parameter(content, n)` walks length-prefixed entries; each entry is
//!   `[2-byte b64 length][bytes...]`. Returns the n-th entry's body.
//! - `get_structured_parameter(content, id)` walks `[id(2)][len(2)][bytes...]`
//!   tuples and returns the first body whose id matches.
//! - `get_parameters(content)` collects every length-prefixed entry.
//!
//! All functions tolerate truncated/malformed input by returning an empty
//! slice, matching the swallowed catch blocks in the C# code.

const OFFSET: u8 = 0x40;

/// Encode `i` as 2 base64 bytes (high 6 bits then low 6 bits, each `+ 0x40`).
///
/// `i` is masked to its low 12 bits, matching the C# behavior of writing
/// exactly two characters via `(i >> 6) & 0x3f` and `i & 0x3f`.
pub fn encode(i: i32) -> [u8; 2] {
    let v = (i as u32) & 0x0fff;
    [OFFSET + ((v >> 6) & 0x3f) as u8, OFFSET + (v & 0x3f) as u8]
}

/// Encode `i` as a 2-byte base64 string returned as `Vec<u8>`.
pub fn encode_vec(i: i32) -> Vec<u8> {
    encode(i).to_vec()
}

/// Decode a base64 (offset 64) big-endian integer from `s`.
///
/// Equivalent to the C# loop:
/// ```text
/// for x in (len-1)..=0:
///   v += (s[x] - 64) * 64.pow(len-1-x)
/// ```
/// Each digit mirrors the C# `(int)(byte)(val[x] - 64)`: the subtraction is
/// evaluated in a wider type and then truncated back through a `(byte)` cast,
/// so any input byte below `0x40` wraps *up* into `0..=255` rather than going
/// negative. `wrapping_sub` on the `u8` reproduces that cast exactly. For
/// canonical base64 (bytes `0x40..=0x7f`, as produced by [`encode`] and sent
/// by real clients) the digit is simply `0..=63` and the wrap never triggers.
pub fn decode(s: &[u8]) -> i32 {
    let mut total: i32 = 0;
    let mut multiplier: i32 = 1;
    for &b in s.iter().rev() {
        let digit = b.wrapping_sub(OFFSET) as i32;
        total = total.wrapping_add(digit.wrapping_mul(multiplier));
        multiplier = multiplier.wrapping_mul(64);
    }
    total
}

/// Returns the body of the n-th length-prefixed parameter, or `&[]` if not
/// found / malformed.
pub fn get_parameter(content: &[u8], n: usize) -> &[u8] {
    let mut pos = 0usize;
    let mut idx = 0usize;
    while pos + 2 <= content.len() {
        let len = decode(&content[pos..pos + 2]);
        if len < 0 {
            return &[];
        }
        let len = len as usize;
        let body_start = pos + 2;
        let body_end = body_start.saturating_add(len);
        if body_end > content.len() {
            return &[];
        }
        if idx == n {
            return &content[body_start..body_end];
        }
        pos = body_end;
        idx += 1;
    }
    &[]
}

/// Returns the body of the parameter whose id matches, or `&[]`.
///
/// Each tuple is `[id(2)][len(2)][body...]`.
pub fn get_structured_parameter(content: &[u8], id: i32) -> &[u8] {
    let mut pos = 0usize;
    while pos + 4 <= content.len() {
        let cid = decode(&content[pos..pos + 2]);
        let clen = decode(&content[pos + 2..pos + 4]);
        if clen < 0 {
            return &[];
        }
        let clen = clen as usize;
        let body_start = pos + 4;
        let body_end = body_start.saturating_add(clen);
        if body_end > content.len() {
            return &[];
        }
        if cid == id {
            return &content[body_start..body_end];
        }
        pos = body_end;
    }
    &[]
}

/// Collect every length-prefixed parameter into a Vec of slices.
pub fn get_parameters(content: &[u8]) -> Vec<&[u8]> {
    let mut out = Vec::new();
    let mut pos = 0usize;
    while pos + 2 <= content.len() {
        let len = decode(&content[pos..pos + 2]);
        if len < 0 {
            return out;
        }
        let len = len as usize;
        let body_start = pos + 2;
        let body_end = body_start.saturating_add(len);
        if body_end > content.len() {
            return out;
        }
        out.push(&content[body_start..body_end]);
        pos = body_end;
    }
    out
}

And I realized, in none of these threads did I ever post VL64 for some reason, so you know what, yolo:

Code:
//! VL64 / LV64 ("wire") encoding for signed integers.
//!
//! ## Wire format
//!
//! The first byte holds:
//! - bits 0..2: low 2 bits of `|i|`
//! - bit  2:    sign (0 = positive, 1 = negative)
//! - bits 3..6: byte-count of the encoding (1..=6)
//! - bits 6..8: always `01` (the `0x40` offset; first byte is `0x40 | …`)
//!
//! Continuation bytes contain 6 bits of `|i|` each, little-endian, also
//! offset by `0x40`. The encoded length matches the C# reference exactly,
//! including the off-by-one quirk where `i = 0` produces a single byte
//! `0x48` ("H"), and small `|i|` < 4 also produce a single byte.
//!
//! ## Boolean shortcut
//!
//! `Encode(true)` yields `'I'` (0x49) and `Encode(false)` yields `'H'`
//! (0x48). These match the single-byte VL64 encodings of `+1` and `0`
//! respectively, which is *why* the convention works.
//!
//! ## Decoding
//!
//! `decode_one` reads the first byte, takes the encoded byte-count, and
//! consumes that many bytes total — returning the value plus how many
//! bytes were consumed so callers can advance their cursor. Truncated or
//! invalid input yields `(0, 0)`, matching the C# `catch { return 0; }`.

/// Encode `i` as a VL64 byte sequence (1..=6 bytes).
pub fn encode(i: i32) -> Vec<u8> {
    // `Math.Abs(int.MinValue)` throws OverflowException in C#, which the
    // reference catches and turns into `""`. Match that exactly — i32::MIN
    // has no positive representation in i32 and would otherwise overflow.
    if i == i32::MIN {
        return Vec::new();
    }
    let mut out = Vec::with_capacity(6);
    let negative_mask: u8 = if i >= 0 { 0 } else { 4 };
    let mag = i.unsigned_abs();

    out.push(0x40 + (mag & 3) as u8);
    let mut bytes_written: u8 = 1;
    let mut remaining = mag >> 2;
    while remaining != 0 {
        bytes_written += 1;
        out.push(0x40 + (remaining & 0x3f) as u8);
        remaining >>= 6;
    }

    out[0] |= bytes_written << 3 | negative_mask;
    out
}

/// Encode a boolean as a single VL64-compatible byte.
pub fn encode_bool(b: bool) -> u8 {
    if b {
        b'I'
    } else {
        b'H'
    }
}

/// Encode each integer in `xs` and concatenate the results.
pub fn encode_many(xs: &[i32]) -> Vec<u8> {
    let mut out = Vec::with_capacity(xs.len() * 2);
    for &x in xs {
        out.extend_from_slice(&encode(x));
    }
    out
}

/// Decode the first VL64 integer from `s`, returning `(value, bytes_consumed)`.
///
/// On malformed/truncated input, returns `(0, 0)`.
pub fn decode_one(s: &[u8]) -> (i32, usize) {
    if s.is_empty() {
        return (0, 0);
    }
    let head = s[0];
    let negative = (head & 4) == 4;
    let total_bytes = ((head >> 3) & 7) as usize;
    if total_bytes == 0 || total_bytes > s.len() {
        return (0, 0);
    }
    let mut value: i32 = (head & 3) as i32;
    let mut shift: u32 = 2;
    for (b, &cont) in s.iter().enumerate().take(total_bytes).skip(1) {
        value |= ((cont & 0x3f) as i32) << shift;
        shift = 2 + 6 * b as u32;
    }
    if negative {
        value = -value;
    }
    (value, total_bytes)
}

/// Decode a VL64 integer assumed to span the whole buffer, falling back to 0.
pub fn decode(s: &[u8]) -> i32 {
    decode_one(s).0
}

/// Decode `'I'` / `'H'` boolean.
pub fn decode_bool_byte(b: u8) -> bool {
    b == b'I'
}

/// Decode a buffer that is a concatenated stream of VL64 ints.
pub fn decode_stream(mut s: &[u8]) -> Vec<i32> {
    let mut out = Vec::new();
    while !s.is_empty() {
        let (v, n) = decode_one(s);
        if n == 0 {
            break;
        }
        out.push(v);
        s = &s[n..];
    }
    out
}

All code is licensed under my lightly modified Bobba Public License v2.1 below:

Code:
BOBBA PUBLIC LICENSE
Version 2.1 (permissive, credit required, not copyleft)

Copyright (c) 2007-2026 Moogly

"Bobba" is what the filter used to say when you swore in the Hotel.
This license says the same thing about ripping out credit.

0. LOBBY DECOR (not operative)

   These words are for atmosphere. They do not add conditions.

   "Software"     Original code and docs you actually wrote.
                  Not official clients, SWFs, furni graphics,
                  sound banks, or anything Sulake owns.

   "Hotel"        A retro, private server, emulator box, or
                  whatever you are calling it this month.

   "Club Sofa"    The traditional unit of thanks. A pint also
                  clears the trade window. A virtual sofa only
                  counts if it is actually rare.

   "Door Tile"    The square directly inside the room entrance.
                  Sitting on it is a Habbo human right. This
                  license does not require you to protect it.
                  It just wants you to know the author believes
                  in it.

   "Throne"       Not included. Do not write in.

1. GRANT

   Permission is hereby granted, free of charge, to any person
   obtaining a copy of this software and associated documentation
   files (the "Software"), to deal in the Software without
   restriction, including without limitation the rights to use,
   copy, modify, merge, publish, distribute, sublicense, and/or
   sell copies of the Software, and to permit persons to whom
   the Software is furnished to do so, subject only to Section 2.

   This is NOT copyleft. You do not have to open your Hotel,
   wired packs, CMS, themes, or modifications. Rename it. Charge
   for your own work. Go private. Fine.

2. CREDIT (the actual rare)

   Keep the copyright notice and a copy of this license, or a
   working link to it, with all copies or substantial portions
   of the Software.

   If you ship a Hotel, emulator, CMS, plugin pack, or other
   product that includes a substantial portion of the Software,
   give visible credit in at least one place operators or
   players will actually see (LICENSE/README, website footer,
   motd, credits room, or Discord #info). A one-liner is enough:

     Based on work by [your name] ([repo or profile URL]).
     Licensed under the Bobba Public License.

   You may add your own copyright for your modifications.
   Credit stacks, like inventory. It does not have to be exclusive.

   Do not strip the notice and present an unmodified or
   trivially renamed copy as solely your original work.
   That is not a fork. That is swapping the plaque on a Throne
   you did not catalogue.

3. NOT REQUIRED (nice furniture, optional)

   You do not have to:
     - release your source
     - keep this license on your derivatives
     - pay anyone
     - leave the door tile empty
     - implement a respect button
     - put "powered by" on the loading screen

   If you get rich in credits, duckets, PayPal, or inexplicable
   Throne wealth, a Club Sofa or a pint is classy. Skipping it
   does not bobba this license and does not void Section 1.

4. TERMINATION

   Distribute without the credit in Section 2, and the grant in
   Section 1 terminates as to you 14 days after notice unless
   you put the credit back. People who already got a copy from
   you in good faith keep their rights.

   Shipping malware, stealers, or "free rare if you log in here"
   credential hooks in a distribution of the Software terminates
   your license immediately. That is not a filter warning.
   That is a kick from the Hotel.

5. DISCLAIMER

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
   KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
   WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
   PURPOSE AND NONINFRINGEMENT.

   THE AUTHOR DOES NOT WARRANT THAT THE SOFTWARE WILL KEEP YOUR
   RARES, THAT WIRED WILL FIRE IN THE ORDER YOU STACKED IT,
   THAT THE CATALOGUE WILL NOT DUPLICATE A THRONE, THAT DUCKETS
   WILL CLEAR AT RESET, OR THAT YOUR PLAYERS WILL STOP ASKING
   FOR FREE HC.

   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM,
   DAMAGES, LOST FURNI, ROLLBACKS, CORRUPT ION TABLES, TRADE
   WINDOW REGRETS, DISCORD DRAMA, OR OTHER LIABILITY, WHETHER
   IN CONTRACT, TORT, OR OTHERWISE, ARISING FROM THE SOFTWARE
   OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

   Section 0 is lore. Grant, credit, termination, and this
   disclaimer are the terms. This is not legal advice.

Anyway, back to the vibes.

See you, Space Cowboy.

...and remember, archive everything, share it with others who archive, hell share it with me, or it will be lost when your hard drive fails, or a file host dies: rapidshare, megaupload, etc all died or their links all died, so make back ups, then make more back ups, then share them with me (or Quackster).


ttfn but expect me to dump some things sooner or later,

~ Moogly
 
Last edited:
Back