API Design

/* --- Device Enumeration --- */

/* Port A-D (0-3), Slot 1-2 (0-1) */
typedef struct {
    uint8_t port;    /* 0=A, 1=B, 2=C, 3=D */
    uint8_t slot;    /* 0=main, 1=secondary */
} sh_vmu_addr_t;

/* Get VMU device by port/slot. Returns NULL if nothing plugged in. */
maple_device_t *sh_vmu_at(sh_vmu_addr_t addr);

/* Get the first available VMU (for single-player games). */
maple_device_t *sh_vmu_first(void);

/* Count connected VMUs. */
int sh_vmu_count(void);

/* Iterate all connected VMUs. Callback returns 0 to continue, non-zero to stop. */
void sh_vmu_each(int (*cb)(sh_vmu_addr_t addr, maple_device_t *dev, void *ctx), void *ctx);


/* --- Screen Rendering --- */

/* All screen functions wrap vmufb_t. The fb is caller-owned. */

/* Clear the VMU framebuffer. */
void sh_vmu_clear(vmufb_t *fb);

/* Draw raw 1-bit pixel data into a region. */
void sh_vmu_blit(vmufb_t *fb, int x, int y, int w, int h, const uint8_t *data);

/* Draw XBM image data. */
void sh_vmu_blit_xbm(vmufb_t *fb, int x, int y, int w, int h, const uint8_t *xbm);

/* Print text using current font. */
void sh_vmu_print(vmufb_t *fb, int x, int y, const char *text);

/* Printf variant. */
void sh_vmu_printf(vmufb_t *fb, int x, int y, const char *fmt, ...);

/* Push framebuffer to a specific VMU. */
void sh_vmu_present(const vmufb_t *fb, sh_vmu_addr_t addr);

/* Push framebuffer to ALL connected VMUs. */
void sh_vmu_present_all(const vmufb_t *fb);

/* Set VMU font. NULL = built-in. Returns previous. */
const vmufb_font_t *sh_vmu_set_font(const vmufb_font_t *font);


/* --- Save Game Management --- */

typedef struct {
    char            name[13];       /* Save file name (12 chars + null) */
    char            desc_short[20]; /* Short description (VMU file manager) */
    char            desc_long[36];  /* Long description (DC BIOS menu) */
    char            app_id[20];     /* Application ID */
    const uint8_t  *icon_data;      /* 32x32 icon (512 bytes per frame, 4bpp) */
    int             icon_frames;    /* Number of animated icon frames */
    uint16_t        icon_palette[16]; /* ARGB4444 palette */
} sh_save_info_t;

typedef enum {
    SH_SAVE_OK = 0,
    SH_SAVE_NO_VMU,          /* No VMU at that address */
    SH_SAVE_NO_SPACE,        /* Not enough free blocks */
    SH_SAVE_NOT_FOUND,       /* Save file doesn't exist */
    SH_SAVE_READ_ERROR,      /* Hardware read failure */
    SH_SAVE_WRITE_ERROR,     /* Hardware write failure */
    SH_SAVE_CRC_MISMATCH,    /* Loaded save has bad CRC */
} sh_save_result_t;

/* Check if a save file exists on a VMU. */
sh_save_result_t sh_save_exists(sh_vmu_addr_t addr, const char *name);

/* Get free blocks on a VMU. Returns -1 on error. */
int sh_save_free_blocks(sh_vmu_addr_t addr);

/* Write a save file. data_len in bytes (will be padded to 512-byte blocks).
   Overwrites existing save with same name. */
sh_save_result_t sh_save_write(sh_vmu_addr_t addr,
                                const sh_save_info_t *info,
                                const void *data, int data_len);

/* Read a save file. *out_data allocated via malloc, caller frees. */
sh_save_result_t sh_save_read(sh_vmu_addr_t addr,
                               const char *name,
                               void **out_data, int *out_len);

/* Delete a save file. */
sh_save_result_t sh_save_delete(sh_vmu_addr_t addr, const char *name);

/* Load .ico file for save icon (KOS vmu_pkg_load_icon wrapper). */
sh_save_result_t sh_save_load_icon(sh_save_info_t *info, const char *ico_path);