135 lines
3.7 KiB
Elixir
135 lines
3.7 KiB
Elixir
defmodule Odinsea.Util.BitTools do
|
|
@moduledoc """
|
|
Utility functions for bit and byte manipulation.
|
|
Provides helper functions for working with binary data, similar to Java's BitTools.
|
|
|
|
Ported from: src/tools/BitTools.java
|
|
"""
|
|
|
|
use Bitwise
|
|
|
|
@doc """
|
|
Reads a 16-bit short integer (little-endian) from a byte array at the given index.
|
|
|
|
## Parameters
|
|
- array: Binary array
|
|
- index: Starting position (0-based)
|
|
|
|
## Returns
|
|
- 16-bit integer value (0-65535)
|
|
"""
|
|
@spec get_short(binary(), non_neg_integer()) :: non_neg_integer()
|
|
def get_short(array, index) when is_binary(array) do
|
|
<<_skip::binary-size(index), value::little-unsigned-16, _rest::binary>> = array
|
|
value
|
|
end
|
|
|
|
@doc """
|
|
Reads a string from a byte array at the given index with specified length.
|
|
|
|
## Parameters
|
|
- array: Binary array
|
|
- index: Starting position
|
|
- length: Number of bytes to read
|
|
|
|
## Returns
|
|
- String extracted from the byte array
|
|
"""
|
|
@spec get_string(binary(), non_neg_integer(), non_neg_integer()) :: String.t()
|
|
def get_string(array, index, length) when is_binary(array) do
|
|
<<_skip::binary-size(index), string_data::binary-size(length), _rest::binary>> = array
|
|
to_string(string_data)
|
|
end
|
|
|
|
@doc """
|
|
Reads a MapleStory-convention string from a byte array.
|
|
Format: 2-byte little-endian length prefix + string data
|
|
|
|
## Parameters
|
|
- array: Binary array
|
|
- index: Starting position
|
|
|
|
## Returns
|
|
- String extracted from the byte array
|
|
"""
|
|
@spec get_maple_string(binary(), non_neg_integer()) :: String.t()
|
|
def get_maple_string(array, index) when is_binary(array) do
|
|
length = get_short(array, index)
|
|
get_string(array, index + 2, length)
|
|
end
|
|
|
|
@doc """
|
|
Rotates bits of a byte left by count positions.
|
|
|
|
## Parameters
|
|
- byte_val: Byte value (0-255)
|
|
- count: Number of positions to rotate
|
|
|
|
## Returns
|
|
- Rotated byte value
|
|
"""
|
|
@spec roll_left(byte(), non_neg_integer()) :: byte()
|
|
def roll_left(byte_val, count) when is_integer(byte_val) and byte_val >= 0 and byte_val <= 255 do
|
|
tmp = byte_val &&& 0xFF
|
|
rotated = tmp <<< rem(count, 8)
|
|
((rotated &&& 0xFF) ||| (rotated >>> 8)) &&& 0xFF
|
|
end
|
|
|
|
@doc """
|
|
Rotates bits of a byte right by count positions.
|
|
|
|
## Parameters
|
|
- byte_val: Byte value (0-255)
|
|
- count: Number of positions to rotate
|
|
|
|
## Returns
|
|
- Rotated byte value
|
|
"""
|
|
@spec roll_right(byte(), non_neg_integer()) :: byte()
|
|
def roll_right(byte_val, count) when is_integer(byte_val) and byte_val >= 0 and byte_val <= 255 do
|
|
tmp = byte_val &&& 0xFF
|
|
rotated = (tmp <<< 8) >>> rem(count, 8)
|
|
((rotated &&& 0xFF) ||| (rotated >>> 8)) &&& 0xFF
|
|
end
|
|
|
|
@doc """
|
|
Repeats the first `count` bytes of `input` `mul` times.
|
|
|
|
## Parameters
|
|
- input: Binary input
|
|
- count: Number of bytes to repeat from the input
|
|
- mul: Number of times to repeat
|
|
|
|
## Returns
|
|
- Binary with repeated bytes
|
|
"""
|
|
@spec multiply_bytes(binary(), non_neg_integer(), non_neg_integer()) :: binary()
|
|
def multiply_bytes(input, count, mul) when is_binary(input) do
|
|
# Take first `count` bytes
|
|
chunk = binary_part(input, 0, min(count, byte_size(input)))
|
|
|
|
# Repeat `mul` times
|
|
1..mul
|
|
|> Enum.map(fn _ -> chunk end)
|
|
|> IO.iodata_to_binary()
|
|
end
|
|
|
|
@doc """
|
|
Converts a double-precision float to a short by extracting high bits.
|
|
|
|
## Parameters
|
|
- d: Double-precision float
|
|
|
|
## Returns
|
|
- 16-bit integer extracted from the high bits
|
|
"""
|
|
@spec double_to_short_bits(float()) :: integer()
|
|
def double_to_short_bits(d) when is_float(d) do
|
|
# Convert double to 64-bit integer representation
|
|
<<long_bits::signed-64>> = <<d::float>>
|
|
|
|
# Extract high 16 bits (shift right by 48)
|
|
(long_bits >>> 48) &&& 0xFFFF
|
|
end
|
|
end
|