extern VOID
log(CHAR16 *fmt, ...);

#define UNUSED __attribute__((__unused__))
#define PACKED __attribute__((packed))

#define _DEFINE_CLEANUP_FUNCTION0(Type, name, func)                                                \
    static inline VOID name(VOID *v)                                                               \
    {                                                                                              \
	if (*(Type *)v)                                                                            \
	    func(*(Type *)v);                                                                      \
    }
_DEFINE_CLEANUP_FUNCTION0(VOID *, _FreePool_p, FreePool)
#define _cleanup_free __attribute__((cleanup(_FreePool_p)))

static inline VOID *
_steal_pointer(VOID *pp)
{
    VOID **ptr = (VOID **)pp;
    VOID *ref = *ptr;
    *ptr = NULL;
    return ref;
}

static inline EFI_STATUS
efi_calculate_crc32(VOID *data, UINTN size, UINT32 *out)
{
    return uefi_call_wrapper(BS->CalculateCrc32, 3, data, size, out);
}

static inline EFI_STATUS
efi_open_protocol(EFI_HANDLE handle,
		  EFI_GUID *guid,
		  VOID *iface,
		  EFI_HANDLE agent,
		  EFI_HANDLE controller,
		  UINT32 attrs)
{
    return uefi_call_wrapper(BS->OpenProtocol, 6, handle, guid, iface, agent, controller, attrs);
}

static inline EFI_STATUS
efi_close_protocol(EFI_HANDLE handle, EFI_GUID *guid, EFI_HANDLE agent)
{
    return uefi_call_wrapper(BS->CloseProtocol, 4, handle, guid, agent, 0);
}

static inline EFI_STATUS
efi_read_blocks(EFI_BLOCK_IO_PROTOCOL *blockio, UINT32 start, UINT32 size, UINT8 *buffer)
{
    return uefi_call_wrapper(blockio->ReadBlocks,
			     5,
			     blockio,
			     blockio->Media->MediaId,
			     start,
			     size,
			     buffer);
}

static inline EFI_STATUS
efi_write_blocks(EFI_BLOCK_IO_PROTOCOL *blockio, UINT32 start, UINT32 size, UINT8 *buffer)
{
    return uefi_call_wrapper(blockio->WriteBlocks,
			     5,
			     blockio,
			     blockio->Media->MediaId,
			     start,
			     size,
			     buffer);
}

typedef struct {
    EFI_HANDLE handle;
    EFI_GUID *guid;
    EFI_HANDLE agent;
    VOID *iface;
} Protocol;

#define PROTOCOL_INIT {0}

static inline VOID
cleanup_protocol_p(VOID *v)
{
    Protocol *protocol = v;
    if (protocol->handle != 0) {
	EFI_STATUS rc;

	rc = efi_close_protocol(protocol->handle, protocol->guid, protocol->agent);
	if (EFI_ERROR(rc)) {
	    log(L"Could not close protocol");
	}
	protocol->handle = 0;
    }
}

#define _cleanup_protocol __attribute__((cleanup(cleanup_protocol_p)))

static inline EFI_STATUS
efi_open_protocol_exclusive(EFI_HANDLE handle, EFI_GUID *guid, EFI_HANDLE agent, Protocol *protocol)
{
    EFI_STATUS rc;
    VOID *iface;

    SetMem(protocol, sizeof(Protocol), 0);

    rc = efi_open_protocol(handle, guid, &iface, agent, 0, EFI_OPEN_PROTOCOL_EXCLUSIVE);
    if (EFI_ERROR(rc)) {
	return rc;
    }

    protocol->handle = handle;
    protocol->guid = guid;
    protocol->agent = agent;
    protocol->iface = iface;

    return EFI_SUCCESS;
}

#define EFI_PARTITION_INFO_PROTOCOL_GUID                                                           \
    {0x8cf2f62c, 0xbc9b, 0x4821, {0x80, 0x8d, 0xec, 0x9e, 0xc4, 0x21, 0xa1, 0xa0}}
#define PARTITION_INFO_PROTOCOL EFI_PARTITION_INFO_PROTOCOL_GUID

#define EFI_PARTITION_INFO_PROTOCOL_REVISION 0x0001000
#define PARTITION_TYPE_OTHER		     0x00
#define PARTITION_TYPE_MBR		     0x01
#define PARTITION_TYPE_GPT		     0x02

#pragma pack(1)

typedef struct {
    UINT32 Revision;
    UINT32 Type;
    UINT8 System;
    UINT8 Reserved[7];
    union {
	MBR_PARTITION_RECORD Mbr;
	EFI_PARTITION_ENTRY Gpt;
    } Info;
} EFI_PARTITION_INFO_PROTOCOL;

#pragma pack()

#ifndef EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID
#define EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID                                                 \
    {0xbc62157e, 0x3e33, 0x4fec, {0x99, 0x20, 0x2d, 0x3b, 0x36, 0xd7, 0x50, 0xdf}}
#endif
