import base64
import os
from struct import pack, calcsize

def getBytesWithSize(data):
    """
    Read length-prefixed data.
    Format: [size:4 bytes big-endian][data:size bytes]
    Returns: (data_bytes, remaining_bytes)
    """
    if len(data) < 4:
        print(f"[WARN] getBytesWithSize: datos insuficientes ({len(data)} < 4)")
        return b"", data
    
    size = int.from_bytes(data[0:4], 'big')  # Explicitly use big-endian
    data = data[4:]
    
    # Validate size (reasonable limit: 100MB)
    MAX_REASONABLE_SIZE = 100 * 1024 * 1024
    if size > MAX_REASONABLE_SIZE:
        print(f"[WARN] getBytesWithSize: tamaño sospechoso ({size} bytes, max razonable: {MAX_REASONABLE_SIZE})")
        # Removed hex dump - use debug logging if needed
        # If size is suspicious, maybe it's actually a marker byte
        # Return empty output and keep all data as remaining
        return b"", data
    
    if len(data) < size:
        print(f"[WARN] getBytesWithSize: tamaño ({size}) mayor que datos disponibles ({len(data)})")
        # Return what we can and empty remaining
        return data[:size], b""
    
    return data[:size], data[size:]


class Packer:
    """
    Packed data into length-prefixed binary format.
    
    Reference: From Havoc framework https://github.com/HavocFramework/Modules/blob/main/Packer/packer.py
    Adapted from Xenon's implementation
    """
    def __init__(self):
        self.buffer : bytes = b''
        self.size   : int   = 0

    def getbuffer(self):
        """Returns length-prefixed buffer: [size:4 bytes little-endian][data]"""
        return pack("<L", self.size) + self.buffer

    def addstr(self, s):
        """Add a null-terminated string"""
        if s is None:
            s = ''
        if isinstance(s, str):
            s = s.encode("utf-8")
        fmt = "<L{}s".format(len(s) + 1)
        self.buffer += pack(fmt, len(s)+1, s)
        self.size   += calcsize(fmt)

    def addWstr(self, s):
        """Add a null-terminated wide string (UTF-16 LE)"""
        if s is None:
            s = ''
        s = s.encode("utf-16_le")
        fmt = "<L{}s".format(len(s) + 2)
        self.buffer += pack(fmt, len(s)+2, s)
        self.size   += calcsize(fmt)

    def addbytes(self, b):
        """Add raw bytes"""
        if b is None:
            b = b''
        fmt = "<L{}s".format(len(b))
        self.buffer += pack(fmt, len(b), b)
        self.size   += calcsize(fmt)

    def addbool(self, b):
        """Add a boolean as int32"""
        fmt = '<I'
        self.buffer += pack(fmt, 1 if b else 0)
        self.size   += 4

    def adduint32(self, n):
        """Add an unsigned int32"""
        fmt = '<I'
        self.buffer += pack(fmt, n)
        self.size   += 4

    def addint(self, dint):
        """Add a signed int32"""
        self.buffer += pack("<i", dint)
        self.size   += 4

    def addshort(self, n):
        """Add a signed int16"""
        fmt = '<h'
        self.buffer += pack(fmt, n)
        self.size   += 2