'OHRRPGCE - the graphics, audio and user input library!
'(C) Copyright 1997-2025 James Paige, Ralph Versteegen, and the OHRRPGCE Developers
'Dual licensed under the GNU GPL v2+ and MIT Licenses. Read LICENSE.txt for terms and disclaimer of liability.

#IFNDEF ALLMODEX_BI
#DEFINE ALLMODEX_BI

#include "config.bi"
#include "const.bi"
#include "reload.bi"
#include "bitmap.bi"
#include "gfx.bi"
#include "surface.bi"
#include "matrixMath.bi"
#include "lib/gif.bi"
#include "music.bi"
#include "animations.bi"


'This Type is misnamed. But currently, a Palette16 virtually always has numcolors=16
Type Palette16
	numcolors as int32
	refcount as int32 'Always >= 1 (palcache counts as a reference). Can not be NOREFC.
	palnum as int32   '>= 0: numbered palette, cached. -1: not loaded from file, uncached.
	col(255) as ubyte 'indices into the master palette
End Type

Type SpriteCacheEntryFwd as SpriteCacheEntry
Type SpriteSetFwd as SpriteSet

const NOREFC = -1234   'allmodex internal use

'An 8 bit, single frame of a sprite.
'Don't forget to update definition in allmodex.h when changing this!!
'As a rather ugly hack (TODO: remove), arrays of Frames are sometimes (for sprite sets) allocated contiguously,
'with each having pointers to separate .image and .mask buffers. All will initially have .refcount = 1,
'.arraylen set to the length of the array, and all but first will have .arrayelem = YES.
'WARNING: don't add strings to this
Type Frame
	Union
		Type
			w as int32
			h as int32
		End Type
		size as XYPair
	End Union

	offset as XYPair   'Draw offset from the position passed to frame_draw. Not used yet.
	pitch as int32     'pixel (x,y) is at .image[.x + .pitch * .y]; mask and image pitch are the same!
	image as ubyte ptr 'Pointer to top-left corner. NULL if and onlf it Surface-backed.
	mask as ubyte ptr  'Same shape as image. If not NULL, nonzero bytes in mask are opaque, rather
	                   'than nonzero bytes in image. Most Frames don't have a mask.
	refcount as int32  'see frame_unload in particular for documentation
			   'Must be NOREFC if allocated on stack rather than heap (frame_load/etc)
	arraylen as int32  'how many frames were contiguously allocated in this frame array
	frameid as int32   'Used by frames in a frameset (always in increasing order): alternative to frame number
	base as Frame ptr    'if a view, the Frame which actually owns this memory
	cacheentry as SpriteCacheEntryFwd ptr  'First Frame in array only
	cached:1 as int32  '(not set for views onto cached sprites) integer, NOT bool! First Frame in array only.
	arrayelem:1 as int32  'not the first frame in a Frame array
	isview:1 as int32  'View of another Frame (which might be backed by a Surface, in which we will be
	                   'backed by a Surface too, created with gfx_surfaceCreateView).
	                   'Aside from that, this is NOT true for Surface-backed Frames which aren't views!
	                   'If this is a view, then 'image' and 'mask' mustn't be freed, but 'surf' must be.
	noresize:1 as int32  '(Video pages only.) Don't resize this page to the window size
	fixeddepth:1 as int32 '(Video pages only.) Not affected by switch_to_32bit/8bit_vpages: Not a render target.
	generation as int32  '(For Frames in the sprite cache) Should be incremented to inform that the Frame
			     'may have been modified/reloaded.

	surf as Surface ptr  'If not NULL, this is a Surface-backed Frame, and image/mask are NULL,
	                     'but all other members are correct (including .pitch), and match the Surface.
	                     '(View of a WHOLE Surface.) Holds a single reference to surf.

	sprset as SpriteSetFwd ptr  'if not NULL, this Frame array is part of a SpriteSet which
                                    'will need to be freed at the same time
				    'First Frame in array only.
	defpal as int32    'Default palette or -1 if not loaded. Only set on first frame of array!
End Type

' You can declare vectors of type "Frame ptr vector".
' When you remove a Frame ptr from the vector, frame_unload is called (decrementing the refcount),
' so likewise when you add a Frame ptr frame_reference is called to increment the refcount.
' ** WARNING: This means you have to call frame_unload to decrement the refcount after appending it! **
DECLARE_VECTOR_OF_TYPE(Frame ptr, Frame_ptr)

Type GraphicPair
	sprite as Frame ptr
	pal as Palette16 ptr
End Type

'Information for a specific group of frames (frames xx, 1xx, 2xx, etc), typically shared between
'all spritesets of a certain type
'(Not used, probably won't be)
Type FrameGroupInfo
	frameid as integer
	name as string
	default_num as integer  'Number of frames to create in new spritesets, 0 for none

	DECLARE SUB set(frameid as integer, name as string, default_num as integer)
End Type

'==========================================================================================
'                              Video pages and video mode

DECLARE SUB setmodex ()
DECLARE SUB restoremode ()
DECLARE FUNCTION switch_gfx (backendname as string) as bool
DECLARE SUB before_gfx_backend_init ()
DECLARE FUNCTION allmodex_setoption(opt as string, arg as string) as integer
DECLARE SUB flush_gfx_config_settings ()

DECLARE SUB switch_to_32bit_vpages ()
DECLARE SUB switch_to_8bit_vpages ()
DECLARE FUNCTION toggle_32bit_vpages () as bool
DECLARE FUNCTION vpages_are_32bit () as bool
DECLARE FUNCTION allocatepage(w as integer = -1, h as integer = -1, bitdepth as integer = -1) as integer
DECLARE FUNCTION duplicatepage (page as integer) as integer
DECLARE SUB freepage (page as integer)
DECLARE FUNCTION registerpage (spr as Frame ptr) as integer
DECLARE SUB copypage (src as integer, dest as integer)
DECLARE SUB clearpage (page as integer, colour as integer = -1)
DECLARE SUB resizepage (page as integer, w as integer, h as integer)
DECLARE FUNCTION compatpage() as integer
DECLARE SUB lock_page_size (page as integer, w as integer, h as integer)
DECLARE SUB unlock_page_size (page as integer)

DECLARE FUNCTION unlock_resolution (min_w as integer, min_h as integer) as bool
DECLARE SUB lock_resolution ()
DECLARE FUNCTION resolution_unlocked () as bool
DECLARE SUB set_resolution (w as integer, h as integer)
DECLARE FUNCTION get_resolution () as XYPair
DECLARE SUB get_screen_size (byref screenwidth as integer, byref screenheight as integer)
DECLARE SUB set_scale_factor (scale as integer, change_windowsize as bool = YES)
DECLARE SUB toggle_fps_display ()

DECLARE FUNCTION windowed_platform() as bool
DECLARE FUNCTION supports_fullscreen_toggling_well () as bool
DECLARE FUNCTION try_check_fullscreen(byref fullscreen as bool) as bool

DECLARE SUB setvispage (page as integer, skippable as bool = YES)
DECLARE SUB fadetopage(oldpage as integer, newpage as integer, fadems as integer = 500)
DECLARE FUNCTION getvispage () as integer
DECLARE SUB setwindowtitle (title as string)
DECLARE SUB setdrawpal (pal() as RGBcolor)
DECLARE SUB setpal (pal() as RGBcolor)
DECLARE SUB setpal_to_color (col as RGBcolor = TYPE(0))
DECLARE FUNCTION masterpal_has_changed(pal() as RGBcolor) as bool
DECLARE SUB fadetocolor (col as RGBcolor, fadems as integer = 500)
DECLARE SUB fadetopal (pal() as RGBcolor, fadems as integer = 500)
DECLARE SUB fadein (fadems as integer = 500)
DECLARE SUB fadeout OVERLOAD (palidx as integer, fadems as integer = 500)
DECLARE SUB fadeout OVERLOAD (red as integer, green as integer, blue as integer, fadems as integer = 500)


'==========================================================================================

DECLARE SUB show_overlay_message(msg as string, seconds as double = 3.)
DECLARE FUNCTION overlay_message_visible () as bool

DECLARE SUB engine_settings_menu ()

'==========================================================================================
'                                  Tilesets & Maps

Enum TileAnimOperator
 taopEnd = 0
 taopUp = 1
 taopDown = 2
 taopRight = 3
 taopLeft = 4
 taopWait = 5
 taopCheckTag = 6
 taopReset = 7
 taopLoop = 8
 taopShiftTile = 9
 taopLAST = 9
End Enum

Type TileAnimCmd
  op as TileAnimOperator
  arg as integer
  arg2 as integer
End Type

Type TileAnimPattern
  range_start as integer
  disable_tag as integer
  cmd(maxTileAnimCmds) as TileAnimCmd

  'For future use; currently unused. See save_tile_anims
  tileset_num as integer
  range_length as integer = 48
  range_stride as integer = 1
End Type

Type TileAnimState
  cycle as integer 'Current tile offset (tile to show)
  pt as integer    'Step number of the next step in the animation
  skip as integer  'Number of ticks left in current wait
  drawoffset as XYPair 'Offset due to taopShiftTile
End Type

Type TilesetData
  num as integer
  spr as Frame ptr
  tanim(1) as TileAnimPattern
  tanim_state(1) as TileAnimState
End Type

'*** Requires construction + destruction ***
Type TileMap
  Union
    Type
      wide as integer
      high as integer
    End Type
    size as XYPair
  End Union
  data as ubyte ptr
  layernum as integer
End Type

DECLARE FUNCTION readblock (map as TileMap, x as integer, y as integer, default as integer = 112343211) as integer
DECLARE SUB writeblock (map as TileMap, x as integer, y as integer, v as integer)

DECLARE SUB drawmap OVERLOAD (tmap as TileMap, x as integer, y as integer, tileset as TilesetData ptr, p as integer, trans as bool = NO, overheadmode as integer = 0, pmapptr as TileMap ptr = NULL, ystart as integer = 0, yheight as integer = -1, pal as Palette16 ptr = NULL, opts as DrawOptions = def_drawoptions)
DECLARE SUB drawmap OVERLOAD (tmap as TileMap, x as integer, y as integer, tilesetsprite as Frame ptr, tilesetanims as TilesetData ptr = NULL, p as integer, trans as bool = NO, overheadmode as integer = 0, pmapptr as TileMap ptr = NULL, ystart as integer = 0, yheight as integer = -1, largetileset as bool = NO, pal as Palette16 ptr = NULL, opts as DrawOptions = def_drawoptions)
DECLARE SUB drawmap OVERLOAD (tmap as TileMap, x as integer, y as integer, tilesetsprite as Frame ptr, tilesetanims as TilesetData ptr = NULL, dest as Frame ptr, trans as bool = NO, overheadmode as integer = 0, pmapptr as TileMap ptr = NULL, largetileset as bool = NO, pal as Palette16 ptr = NULL, opts as DrawOptions = def_drawoptions)

DECLARE SUB draw_layers_at_tile(composed_tile as Frame ptr, tiles as TileMap ptr vector, tilesets as TilesetData ptr vector, tx as integer, ty as integer, pmapptr as TileMap ptr = NULL)

DECLARE SUB setoutside (defaulttile as integer)


'==========================================================================================
'                                     Drawing

Enum bgType
	bgFIRST = -2
	bgChequer = -2       'Non-scrolling chequered pattern
	bgChequerScroll = -1 'Scrolling chequered pattern
	'0 - 255 are master palette colors
End Enum

DECLARE SUB drawbox OVERLOAD (x as RelPos, y as RelPos, w as RelPos, h as RelPos, col as integer, thickness as integer = 1, p as integer)
DECLARE SUB drawbox OVERLOAD (dest as Frame ptr, x as RelPos, y as RelPos, w as RelPos, h as RelPos, col as integer, thickness as integer = 1)
DECLARE SUB drawcube (dest as Frame ptr, rect as RectType, off as XYPair, col as integer, thickness as integer = 1)
DECLARE sub drawants (dest as Frame ptr, x as RelPos, y as RelPos, wide as RelPos, high as RelPos, color as integer = -1)
DECLARE SUB rectangle OVERLOAD (x as RelPos, y as RelPos, w as RelPos, h as RelPos, c as integer, p as integer)
DECLARE SUB rectangle OVERLOAD (fr as Frame Ptr, x as RelPos, y as RelPos, w as RelPos, h as RelPos, c as integer)
DECLARE sub rectangle OVERLOAD (fr as Frame Ptr, byval rect as RelrectType, c as integer)
DECLARE SUB trans_rectangle (dest as Frame ptr, byval rect as RelRectType, byval col as RGBcolor, alpha as double)
DECLARE SUB fuzzyrect OVERLOAD (x as RelPos, y as RelPos, w as RelPos = rWidth, h as RelPos = rHeight, c as integer, p as integer, fuzzfactor as integer = 50, stationary as bool = NO, zoom as integer = 1, offset as integer = 0)
DECLARE SUB fuzzyrect OVERLOAD (fr as Frame Ptr, byval rect as RelRectType, c as integer, fuzzfactor as integer = 50, stationary as bool = NO, zoom as integer = 1, offset as integer = 0)
DECLARE SUB antifuzzyrect(fr as Frame Ptr, rect as RelRectType, col as integer, fuzzfactor as integer = 50, zoom as integer = 1)

DECLARE SUB draw_background (dest as Frame ptr, bgcolor as bgType = bgChequerScroll, byref chequer_scroll as integer = 0, x as RelPos = 0, y as RelPos = 0, wide as RelPos = rWidth, high as RelPos = rHeight)

Type ClipState
	'Which Frame the clips are set for
	frame as Frame ptr
	'Drawable area on clippedframe; right & bottom edges are INCLUSIVE!
	l as integer
	t as integer
	r as integer
	b as integer
End Type

'NOTE: clipping values are global.
DECLARE SUB setclip(l as integer = 0, t as integer = 0, r as integer = 999999, b as integer = 999999, fr as Frame ptr = 0)
DECLARE FUNCTION shrinkclip(l as integer = 0, t as integer = 0, r as integer = 999999, b as integer = 999999, fr as Frame ptr = 0) as bool
DECLARE FUNCTION get_cliprect(fr as Frame ptr = NULL) BYREF as ClipState

DECLARE SUB putpixel OVERLOAD (spr as Frame ptr, x as integer, y as integer, c as integer)
DECLARE SUB putpixel OVERLOAD (x as integer, y as integer, c as integer, p as integer)
DECLARE FUNCTION readpixel OVERLOAD (spr as Frame ptr, x as integer, y as integer) as integer
DECLARE FUNCTION readpixel OVERLOAD (x as integer, y as integer, p as integer) as integer
DECLARE SUB drawline OVERLOAD (dest as Frame ptr, x1 as integer, y1 as integer, x2 as integer, y2 as integer, c as integer, dash_cycle as integer = 0, dash_len as integer = 0)
DECLARE SUB drawline OVERLOAD (x1 as integer, y1 as integer, x2 as integer, y2 as integer, c as integer, p as integer, dash_cycle as integer = 0, dash_len as integer = 0)

DECLARE SUB paintat (dest as Frame ptr, x as integer, y as integer, c as integer)
DECLARE SUB ellipse (fr as Frame ptr, x as double, y as double, radius as double, c as integer, fillcol as integer = -1, semiminor as double = 0.0, angle as double = 0.0)
DECLARE SUB replacecolor (fr as Frame ptr, c_old as integer, c_new as integer, swapcols as bool = NO)
DECLARE SUB swapcolors(fr as Frame ptr, col1 as integer, col2 as integer)
DECLARE FUNCTION countcolor (fr as Frame ptr, col as integer) as integer
DECLARE SUB remap_to_palette OVERLOAD (fr as Frame ptr, pal as Palette16 ptr)
DECLARE SUB remap_to_palette OVERLOAD (fr as Frame ptr, palmapping() as integer)

DECLARE SUB draw_basic_mouse_cursor (page as integer)
DECLARE FUNCTION draw_keys_overlay(page as integer) as bool

'==========================================================================================

DECLARE SUB storemxs (fil as string, record as integer, fr as Frame ptr)
DECLARE SUB loadmxs (filen as string, record as integer, dest as Frame ptr)
DECLARE FUNCTION mxs_frame_to_tileset(spr as Frame ptr) as Frame ptr

'==========================================================================================

DECLARE SUB setwait (ms as double, flagms as double = 0)
DECLARE FUNCTION dowait () as bool
DECLARE FUNCTION setwait_time_remaining() as double
DECLARE FUNCTION get_tickcount() as integer

'==========================================================================================
'                               Fonts and text rendering

CONST fontPlain = 0   'The game's font
CONST fontEdged = 1
CONST fontShadow = 2
CONST fontBuiltinPlain = 3  'Builtin Latin-1 font
CONST fontBuiltinEdged = 4
CONST fontLAST = 4

Type FontChar
	offset as integer  'offset into spr->image
	offx as byte       'Amount to offset the image when drawing, in pixels
	offy as byte
	w as byte          'Size of sprite
	h as byte
End Type

'A FontLayer can be shared between multiple fonts
Type FontLayer
	spr as Frame ptr        'FIXME: this is meant to be an atlas/spritesheet, but it's actually a
	                        'concatenation of pixel buffers for each character. Needs changing for backend accel
	refcount as integer
	chdata(255) as FontChar

	declare constructor()
	declare constructor(src as FontLayer ptr)
	declare destructor()
End Type

Type Font
	layers(1) as FontLayer ptr	'single layer fonts should use sprite(1) only
	w(255) as integer	'width of each character
	char_h as integer	'nominal height of a character (e.g. for insert cursor)
	line_h as integer	'height of a line (default separation between lines)
	offset as XYPair	'added to coordinates when printing
	cols as integer		'number of used colours, not including colour 0 (transparency), so at most 15
	pal as Palette16 ptr    '(Default) palette template to use, or NULL if this font is unpaletted (foreground colour only)
	outline_col as integer  'palette entry (1 to .cols) which should be filled with uiOutline, or 0 for none.

	declare constructor()
	declare constructor(src as Font ptr)
	'Recommmended to call the font_unload wrapper instead of using delete directly
	declare destructor()
End Type

'text_layout_dimensions returns this struct
Type StringSize
	size as XYPair       'Width is the greatest width of any line
	lineend as integer   '1-based character position of the end of the line containing endchar or endline
	                     'This is the character which was wrapped, usually a space or newline, or first char on next line.
	lastw as integer     'Width of last line fragment
	lasth as integer     'Height of last line fragment
	lines as integer     'Number of lines (always at least 1)   FIXME:  not true
	finalfont as Font ptr
End Type

Type StringCharPos
	charnum as integer   '0-based!! offset in string; equal to len(text) if off the end
	exacthit as bool     'whether actually on this character, or just the nearest (eg. off end of line)
	pos as XYPair        'position is in screen coordinates
	size as XYPair       'Size of the selected character
	lineh as integer     'height of containing line fragment
End Type

Type PrintStrStatePtr as PrintStrState ptr

DECLARE FUNCTION parse_tag (z as string, offset as integer, byref action as string, arg as int32 ptr) as integer

DECLARE SUB printstr (text as string, x as RelPos, y as RelPos, page as integer, withtags as bool = NO, fontnum as integer = fontPlain)
DECLARE SUB edgeprint (text as string, x as RelPos, y as RelPos, col as integer, page as integer, withtags as bool = NO, withnewlines as bool = NO)
DECLARE SUB wrapprint (text as string, x as RelPos, y as RelPos, col as integer = -1, page as integer, wrapx as RelPos = rWidth, withtags as bool = YES, fontnum as integer = fontEdged)
DECLARE SUB wrapprintbg (text as string, x as RelPos, y as RelPos, col as integer = -1, page as integer, drawbg as bool = YES, wrapx as RelPos = rWidth, withtags as bool = YES, fontnum as integer = fontEdged)
DECLARE SUB textcolor (fg as integer, bg as integer)

DECLARE SUB text_layout_dimensions (retsize as StringSize ptr, z as string, endchar as integer = 999999, maxlines as integer = 999999, wide as integer = 999999, fontp as Font ptr, withtags as bool = YES, withnewlines as bool = YES)
DECLARE FUNCTION textwidth(text as string, fontnum as integer = fontPlain, withtags as bool = YES, withnewlines as bool = YES) as integer
DECLARE FUNCTION textsize(text as string, wide as RelPos = rWidth, fontnum as integer = fontPlain, withtags as bool = YES, page as integer = -1) as XYPair
DECLARE FUNCTION str_rect(s as string, byval x as integer, byval y as integer) as RectType
DECLARE FUNCTION lineheight(fontnum as integer = fontEdged) as integer
DECLARE FUNCTION charsize OVERLOAD(char as integer, font as Font ptr) as XYPair
DECLARE FUNCTION charsize OVERLOAD(char as integer, fontnum as integer) as XYPair

DECLARE SUB find_text_char_position(retsize as StringCharPos ptr, text as string, charnum as integer, wide as RelPos = rWidth, fontnum as integer = fontPlain, withtags as bool = YES, page as integer = -1)
DECLARE SUB find_point_in_text (retsize as StringCharPos ptr, seekpt as XYPair, z as string, wide as integer = 999999, draw_pos as XYPair = XY(0,0), fontnum as integer, withtags as bool = YES, withnewlines as bool = YES)

DECLARE FUNCTION fgcol_text (text as string, colour as integer) as string
DECLARE FUNCTION bgcol_text (text as string, colour as integer) as string
DECLARE FUNCTION remove_markup(text as string) as string
DECLARE FUNCTION just_markup(text as string) as string

DECLARE FUNCTION get_font(fontnum as integer, show_err as bool = NO) as Font ptr

DECLARE SUB setfont (ohf_font() as integer)
DECLARE SUB set_builtin_font (ohf_font() as integer)
DECLARE FUNCTION get_font_type (ohf_font() as integer) as fontTypeEnum
DECLARE SUB set_font_type (ohf_font() as integer, ty as fontTypeEnum)
DECLARE SUB font_unload (fontpp as Font ptr ptr)
DECLARE FUNCTION font_create_edged (basefont as Font ptr) as Font ptr
DECLARE FUNCTION font_create_shadowed (basefont as Font ptr, xdrop as integer = 1, ydrop as integer = 1) as Font ptr
DECLARE FUNCTION font_loadbmps (directory as string, fallback as Font ptr = null) as Font ptr
DECLARE FUNCTION font_load_16x16 (filename as string) as Font ptr

'==========================================================================================
'                                    BMPs/GIFs/screenshots

' Options to quantize_surface
TYPE QuantizeOptions
	firstindex as integer    'Pass firstindex = 1 to prevent anything from getting mapped to colour 0. (Default 0)
	transparency as RGBcolor 'Color to map to 0 (should have .a=0) (Default -1, meaning none)
	dither as bool
	dither_maxerror as integer 'How much dithering to do (kGifMaxAccumError); 0 means no dither. (Default 50)
	compute_palette as bool  'If true, pal() is output rather than input!
	to_master as bool        'pal() is curmasterpal() (set by last setpal call). Allows use of nearcolor_fast
	                         '(currently only implemented if dither=NO compute_palette=NO).
	                         'firstindex will be ignored! Always taken as 1.
END TYPE

TYPE ImageFileInfo
	imagetype as ImageFileTypes
	imagetype_name as string
	supported as bool   'Can be loaded
	valid as bool       'Appears to be valid, regardless of being supported
	paletted as bool    'Has a palette. Check this, not bpp!
	alpha as bool       'Has alpha channel or color key
	                    '(Warning: is false for PNGs with palette w/ transparent colors)
	info as string      'Description, whether supported or not
	error as string     'If not supported, error message describing problem
	size as XYPair
	bpp as integer      'Bits per pixel
END TYPE

DECLARE FUNCTION image_file_type (filename as string) as ImageFileTypes
DECLARE FUNCTION image_read_info (filename as string) as ImageFileInfo
DECLARE FUNCTION image_load_palette (filename as string, pal() as RGBcolor) as integer
DECLARE FUNCTION image_import_as_frame_raw (filename as string) as Frame ptr
DECLARE FUNCTION image_import_as_frame_paletted (filename as string, pal() as RGBColor) as Frame ptr
DECLARE FUNCTION image_import_as_frame_quantized (bmp as string, pal() as RGBcolor, options as QuantizeOptions = TYPE(0, -1)) as Frame ptr
DECLARE FUNCTION image_import_as_frame_and_palette16 (byref ret as GraphicPair, filename as string, defaultpal as Palette16 ptr = NULL) as bool
DECLARE FUNCTION image_import_as_frame_8bit (filename as string, masterpal() as RGBcolor, keep_col0 as bool = YES, byval transparency as RGBcolor = TYPE(-1)) as Frame ptr
DECLARE FUNCTION image_import_as_frame_32bit (filename as string) as Frame ptr

DECLARE FUNCTION image_import_as_surface (bmp as string, always_32bit as bool) as Surface ptr
DECLARE SUB frame_export_image (fr as Frame ptr, filename as string, masterpal() as RGBcolor, pal as Palette16 ptr = NULL)
DECLARE SUB surface_export_image (surf as Surface ptr, filename as string)

DECLARE SUB palette_from_16x16_image (filename as string, pal() as RGBcolor)

DECLARE FUNCTION screenshot(basename as string = "", page as integer = -1, message as bool = YES) as string
DECLARE SUB bmp_screenshot(basename as string)
DECLARE SUB start_recording_gif(secondscreen as string = "")
DECLARE FUNCTION recording_gif() as bool
DECLARE SUB toggle_recording_gif()
DECLARE SUB start_forwarding_screen(outfile as string)
DECLARE SUB stop_recording_video()

'Note order of Frame/Surface ptr and filename varies!

DECLARE SUB frame_export_bmp4 (f as string, fr as Frame Ptr, maspal() as RGBcolor, pal as Palette16 ptr)
DECLARE SUB frame_export_bmp8 (f as string, fr as Frame Ptr, maspal() as RGBcolor)
DECLARE SUB surface_export_bmp24 (f as string, surf as Surface Ptr)
DECLARE SUB surface_export_bmp (f as string, surf as Surface Ptr, maspal() as RGBcolor)
DECLARE SUB frame_export_bmp (fname as string, fr as Frame ptr, maspal() as RGBcolor, pal as Palette16 ptr = NULL)

DECLARE FUNCTION frame_import_bmp_raw(bmp as string) as Frame ptr

DECLARE FUNCTION surface_export_jpeg(surf as Surface ptr, filename as string, quality as integer = 95) as bool
DECLARE FUNCTION frame_export_jpeg(fr as Frame ptr, filename as string, masterpal() as RGBcolor, pal as Palette16 ptr = NULL, quality as integer = 95) as bool

DECLARE FUNCTION surface_export_png(surf as Surface ptr, filename as string, masterpal() as RGBcolor, pal as Palette16 ptr = NULL, compress as integer = 1) as bool
DECLARE FUNCTION frame_export_png(fr as Frame ptr, filename as string, masterpal() as RGBcolor, pal as Palette16 ptr = NULL, compress as integer = 1) as bool

'Read BMP info or palette
DECLARE FUNCTION loadbmppal (f as string, pal() as RGBcolor) as integer
DECLARE FUNCTION bmpinfo OVERLOAD (f as string, byref dat as BitmapV3InfoHeader, byref errmsg as string = "") as integer
DECLARE SUB bmpinfo OVERLOAD (filename as string, byref iminfo as ImageFileInfo)

'Color matching
DECLARE FUNCTION color_distance(pal() as RGBcolor, index1 as integer, index2 as integer) as integer
DECLARE FUNCTION nearcolor OVERLOAD (pal() as RGBcolor, red as integer, green as integer, blue as integer, firstindex as integer = 0, indexhint as integer = -1, avoidcol as integer = -1) as ubyte
DECLARE FUNCTION nearcolor OVERLOAD (pal() as RGBcolor, index as integer, firstindex as integer = 0) as ubyte
EXTERN "C"
DECLARE FUNCTION nearcolor_master(byval col as RGBcolor, firstindex as integer = 0) as ubyte
DECLARE FUNCTION nearcolor_fast OVERLOAD (byval col as RGBcolor) as ubyte
END EXTERN
DECLARE FUNCTION nearcolor_fast OVERLOAD (r as integer, g as integer, b as integer) as ubyte
DECLARE SUB find_palette_mapping (inputpal() as RGBcolor, masterpal() as RGBcolor, mapping() as integer, firstindex as integer = 0)
DECLARE FUNCTION quantize_surface(byref surf as Surface ptr, pal() as RGBcolor, options as QuantizeOptions) as Frame ptr
'Never returns 0
#DEFINE findrgb(r, g, b)  nearcolor_fast(r, g, b)

'Export .gifs
DECLARE SUB GifPalette_from_pal (byref gpal as GifPalette, masterpal() as RGBcolor, pal as Palette16 ptr = NULL)
DECLARE SUB surface_export_gif (surf as Surface Ptr, fname as string, dither as bool = NO)
DECLARE SUB frame_export_gif (fr as Frame Ptr, fname as string, maspal() as RGBcolor, pal as Palette16 ptr = NULL, transparent as bool = NO)


'==========================================================================================
'                                          Input

'=================== Keymaps ====================

' Mapping from a scancode to a cc* control code/action. Can be blank.
Type Keybind
	scancode as KBScancode          'Either a keyboard or joystick KBScancode. 0 if blank.
	ckey as ccCode                  'A cc* virtual scancode. 0 if blank.
	suspended as bool               'Suspended keybinds don't cause keypresses
End Type

' A set of keybinds (KB/Joy scancode to ccKey) for one player.
' Can include blank Keybinds, which were removed and are ignored.
' All joystick scancodes refer to that player's gamepad.
Type PlayerKeymap
	controls(any) as Keybind

	declare sub reset (player as integer)
	declare sub add (controlc as ccCode, scanc as KBScancode)
	declare sub remove (cc_or_sc as KBScancode, sc as KBScancode = 0)
	declare sub suspend (cc_or_sc as KBScancode, sc as KBScancode = 0)
	declare sub resume (cc_or_sc as KBScancode, sc as KBScancode = 0)
	declare function find (cc_or_sc as KBScancode, sc as KBScancode = 0, count as integer = 0) as integer
End Type

declare function get_keymap (player as integer = 1) byref as PlayerKeymap
declare sub set_keymap (player as integer = 1, byref keymap as PlayerKeymap)
declare sub reset_keymaps ()
declare sub reset_to_basic_keymap ()

declare sub setkeyrepeat (repeat_wait as integer = 500, repeat_rate as integer = 55)

'=========== Update or wait for keys ============

DECLARE FUNCTION interrupting_keypress () as bool
DECLARE FUNCTION anykeypressed (checkjoystick as bool = YES, checkmouse as bool = YES, trigger_level as KeyBits = 2) as KBScancode
DECLARE FUNCTION waitforanykey (wait_for_resize as bool = NO, trigger_level as KeyBits = 4, clearkeypress as bool = YES) as KBScancode
DECLARE SUB waitforkeyrelease ()

DECLARE SUB setkeys (enable_inputtext as bool = NO)
DECLARE SUB real_clearkey (key as KBScancode, clear_key_repeat as bool = YES)
DECLARE SUB clearkey (key as KBScancode, clear_key_repeat as bool = YES)
DECLARE SUB clearkeys ()

' This SUB is implemented in Game/Custom and called from allmodex.
DECLARE SUB global_setkeys_hook ()

DECLARE SUB setquitflag (newstate as bool = YES)
DECLARE FUNCTION getquitflag () as bool

'============== Recording/replaying =============

DECLARE SUB start_recording_input (filename as string)
DECLARE SUB stop_recording_input (msg as string="", errorlevel as ErrorLevelEnum = errError)
DECLARE SUB start_replaying_input (filename as string, num_repeats as integer = 1)
DECLARE SUB stop_replaying_input (msg as string="", errorlevel as ErrorLevelEnum = errError)
DECLARE SUB pause_replaying_input
DECLARE SUB resume_replaying_input
DECLARE SUB pause_recording_input
DECLARE SUB resume_recording_input

DECLARE SUB macro_controls ()

'=============== Generic keyval ================

DECLARE FUNCTION player_keyval(key as KBScancode, player as integer = 0, byref down_ms as integer = 0, repeat_wait as integer = 0, repeat_rate as integer = 0, check_keyboard as bool = YES, real_keys as bool = NO) as KeyBits
DECLARE FUNCTION real_keyval (key as KBScancode) as KeyBits
DECLARE FUNCTION keyval (key as KBScancode) as KeyBits
DECLARE FUNCTION carray alias "KEYVAL" (key as KBScancode) as KeyBits
DECLARE FUNCTION slowkey (key as KBScancode, ms as integer) as bool
DECLARE FUNCTION getinputtext () as string
DECLARE FUNCTION keypress_time (key as KBScancode, player as integer = 0, real_keys as bool = NO) as integer

'================== Joystick ===================

DECLARE FUNCTION joystick_axis (axis as integer, player as integer = 0) as integer
DECLARE FUNCTION joystick_info (player as integer) as JoystickInfo ptr
DECLARE FUNCTION keybd_to_joy_scancode (key as KBScancode) as JoyButton
DECLARE FUNCTION num_joysticks () as integer
DECLARE SUB disable_joystick_input()
DECLARE SUB enable_joystick_input() 'defaults to enabled, so this doesn't necessarily need to be called

'==================== Mouse =====================

Type MouseInfo
	Union
		Type
			'Note: position usually stops updating when .active is NO.
			'See comments in update_mouse_state documenting backend differences.
			x as integer
			y as integer
		End Type
		pos as XYPair
	End Union
	lastpos as XYPair     'pos, last tick
	moved as bool         'Whether mouse has moved since the last setkeys call
	moved_dist as integer 'Distance moved since last tick
	active as bool        'Is over the window and the window is focused
	clicks as integer     'Button down since the last setkeys call; MouseButton bitvector (see scancodes.bi)
	buttons as integer    'Buttons currently down OR clicked; MouseButton bitvector
	release as integer    'Buttons that were released last click; MouseButton bitvector
	last_buttons as integer 'used when calculating .release; MouseButton bitvector
	wheel as integer      'Wheel position. Each dedent/click is 120, but may change by less than that
	wheel_delta as integer  'Change in .wheel since last setkeys
	wheel_clicks as integer 'Multiples of 120 that .wheel has changed since last setkeys (NOT .wheel_delta\120)
		                'If the wheel is moved very slowly, wheel_delta\120 may be always 0.
	dragging as integer   'MouseButton bitvector, but only one button at once can be dragged.
	                      'A dragged button is one held down for at least 2 ticks.
	                      'So on the first tick, you see click=button=true, dragging=false
	                      'And on the subsequent ticks, you see dragging=button=true, click=false
			      '(TODO: rename to holding)
	drag_dist as integer  'The total distance the mouse has been dragged; check to see whether
	                      'actually dragging rather than holding
	clickstart as XYPair  'Mouse position at start of click/drag (Note: no backend currently
	                      'supports reporting the position of click, so currently equal to .x/.y)
	left_click_age as integer 'Keeps track of how many ticks the left button has been held down.
	                      'includes both when the .buttons bit is set and when the .release bit is set

	DECLARE SUB clearclick(button as MouseButton)
End Type

DECLARE FUNCTION havemouse () as bool
DECLARE SUB hidemousecursor ()
DECLARE SUB showmousecursor ()
DECLARE SUB defaultmousecursor ()
DECLARE SUB setcursorvisibility (state as CursorVisibility)
DECLARE FUNCTION getcursorvisibility () as CursorVisibility
DECLARE FUNCTION readmouse () byref as MouseInfo
DECLARE SUB movemouse (x as integer, y as integer)
DECLARE SUB mouserect (xmin as integer, xmax as integer, ymin as integer, ymax as integer)
DECLARE SUB pause_mouserect ()
DECLARE SUB resume_mouserect ()


'==========================================================================================
'                                  Music and Sound effects

DECLARE SUB setupmusic
DECLARE SUB closemusic ()
DECLARE SUB loadsong (f as string)
DECLARE SUB playsongnum (songnum as integer)
'DECLARE SUB pausesong ()
'DECLARE SUB resumesong ()
DECLARE FUNCTION get_music_volume () as single
DECLARE SUB set_music_volume (vol as single)

DECLARE SUB resetsfx ()
DECLARE SUB playsfx (num as integer, loopcount as integer = 0, volume_mult as single = 1.0)
DECLARE SUB stopsfx (num as integer)
DECLARE SUB pausesfx (num as integer)
DECLARE SUB freesfx (num as integer)
DECLARE FUNCTION sfxisplaying (num as integer) as bool
DECLARE FUNCTION effective_sfx_volume (num as integer) as single
DECLARE SUB set_sfx_volume (num as integer, volume_mult as single)
DECLARE SUB set_global_sfx_volume (volume as single)
DECLARE FUNCTION get_global_sfx_volume () as single


'==========================================================================================
'                                          Frame

declare function graphics_file(filename_or_extn as string) as string

declare function frame_new(w as integer, h as integer, frames as integer = 1, clr as bool = NO, wantmask as bool = NO, with_surface32 as bool = NO, no_alloc as bool = NO) as Frame ptr
declare function frame_new_view(spr as Frame ptr, x as integer, y as integer, w as integer, h as integer) as Frame ptr
declare function frame_load(sprtype as SpriteType, record as integer) as Frame ptr
declare function frame_load_uncached(sprtype as SpriteType, record as integer) as Frame ptr
declare function frame_load_4bit(filen as string, record as integer, numframes as integer, wid as integer, hei as integer) as Frame ptr
declare function frame_load_mxs(filen as string, record as integer) as Frame ptr
declare function frameset_to_node(fr as Frame ptr, parent as Reload.NodePtr) as Reload.NodePtr
declare function frameset_from_node(node as Reload.NodePtr) as Frame ptr
declare function frameid_to_frame(frameset as Frame ptr, frameid as integer, exact as bool = NO) as integer
declare function num_frames_in_group(frameset as Frame ptr, group as integer) as integer

extern "C"
declare function frame_reference (p as Frame ptr) as Frame ptr
declare sub frame_assign(ptr_to_replace as Frame ptr ptr, new_value as Frame ptr)
declare sub frame_unload (p as Frame ptr ptr)
end extern

declare sub frame_draw overload (src as Frame ptr, pal as Palette16 ptr = NULL, x as RelPos, y as RelPos, trans as bool = YES, page as integer, opts as DrawOptions = def_drawoptions)
declare sub frame_draw overload (src as Frame ptr, pal as Palette16 ptr = NULL, x as RelPos, y as RelPos, trans as bool = YES, dest as Frame ptr, opts as DrawOptions = def_drawoptions)
declare sub frame_draw overload (src as Frame ptr, masterpal() as RGBcolor, pal as Palette16 ptr = NULL, x as RelPos, y as RelPos, trans as bool = YES, dest as Frame ptr, opts as DrawOptions = def_drawoptions)
declare sub frame_draw_transformed(src as Frame ptr, masterpal() as RGBcolor, pal as Palette16 ptr = NULL, transf as AffineTransform, trans as bool = YES, dest as Frame ptr, opts as DrawOptions = def_drawoptions, vertex_cols as RGBcolor ptr = NULL)
declare sub rectangle_transformed(cols() as RGBcolor, transf as AffineTransform, dest as Frame ptr, opts as DrawOptions = def_drawoptions)

declare sub rotozoom_transform(byref result as AffineTransform, size as XYPair, center as Float2 ptr = NULL, pos as Float2, angle as double, zoom as Float2)

declare function dissolve_type_caption(n as integer) as string
declare function appear_type_caption(n as integer) as string
declare function frame_dissolved(spr as Frame ptr, tlength as integer, t as integer, style as integer) as Frame ptr
declare sub frame_draw_dissolved (src as Frame ptr, pal as Palette16 ptr = NULL, x as RelPos, y as RelPos, trans as bool = YES, dest as Frame ptr, opts as DrawOptions = def_drawoptions, tlength as integer, tick as integer, style as integer)
declare function default_dissolve_time(style as integer, w as integer, h as integer) as integer
declare function frame_rotozoom(src as Frame ptr, pal as Palette16 ptr = NULL, angle as double, hzoom as double, vzoom as double, smooth as integer = 0) as Frame ptr
declare sub frame_flip_horiz(spr as Frame ptr)
declare sub frame_flip_vert(spr as Frame ptr)
declare function frame_rotated_90(spr as Frame ptr) as Frame ptr
declare function frame_rotated_270(spr as Frame ptr) as Frame ptr
declare function frame_duplicate(p as Frame ptr, clr as bool = NO, addmask as bool = NO) as Frame ptr
declare function frame_resized(spr as Frame ptr, wide as integer, high as integer, shiftx as integer = 0, shifty as integer = 0, bgcol as integer = 0) as Frame ptr
declare function frame_scaled32(src as Frame ptr, wide as integer, high as integer, masterpal() as RGBcolor, pal as Palette16 ptr = NULL) as Frame ptr
declare sub frame_clear(spr as Frame ptr, colour as integer = 0)
declare sub sprite_empty_cache(sprtype as SpriteType = sprTypeInvalid, setnum as integer = -1)
declare sub sprite_update_cache(sprtype as SpriteType)
declare sub cache_all_spritesets(sprtype as SpriteType)
declare sub tileset_empty_cache()
declare function frame_is_valid(p as Frame ptr) as bool
declare sub sprite_debug_cache()
declare function frame_describe(p as Frame ptr) as string

declare function frame_to_surface32(fr as Frame ptr, masterpal() as RGBcolor, pal as Palette16 ptr = NULL) as Surface ptr
declare sub frame_convert_to_32bit(fr as Frame ptr, masterpal() as RGBcolor, pal as Palette16 ptr = NULL)
declare sub frame_drop_surface(fr as Frame ptr)
declare function frame_with_surface(surf as Surface ptr) as Frame ptr


'==========================================================================================
'                                       Palette16

enum ColorMixMethod
	mixBlend
	mixMult
end enum

enum ColorOperator
	copLuminance
	copValue
	copTintValue

	copGreyscale = copLuminance
end enum

declare function palette16_new(numcolors as integer = 16) as Palette16 ptr
declare function Palette16_new_identity(numcolors as integer = 16) as Palette16 ptr
declare function palette16_new_from_indices(pal() as integer) as Palette16 ptr
declare function palette16_load(num as integer, autotype as SpriteType = sprTypeInvalid, spr as integer = 0, expect_exists as bool = YES) as Palette16 ptr
declare function palette16_load_pal_uncached(fil as string, num as integer) as Palette16 ptr
declare sub palette16_unload(p as Palette16 ptr ptr)
declare function palette16_reference(pal as Palette16 ptr) as Palette16 ptr
declare function palette16_duplicate(pal as Palette16 ptr) as Palette16 ptr
declare sub palette16_reload_cache()
declare sub palette16_update_cache(num as integer)
declare function palette16_describe(pal as Palette16 ptr) as string
declare sub Palette16_transform_n_match(pal as Palette16 ptr, method as ColorOperator)
declare sub palette16_mix_n_match(pal as Palette16 ptr, byval col as RGBcolor, colfrac as double, method as ColorMixMethod, scale as double = 1.0)
declare function masterpal_to_gfxpal(pal() as RGBcolor) as RGBPalette ptr


'==========================================================================================
'                                       SpriteSets

'SpriteSet is intended as replacement for holding pointers to arrays of Frames,
'and also holds any animations.
'Each SpriteSet is tied to a Frame array, and the two are always deleted together
'(by frame_freemem), although a Frame array might not have a SpriteSet until
'spriteset_for_frame() is called.
'SpriteSet references need to be managed using ->reference() and spriteset_unload()
Type SpriteSet
	'refcount is set to NOREFC and references are instead tracked with frames->refcount

	frames as Frame ptr    'Never NULL. Does NOT count as a reference
	animset as AnimationSet ptr  'May be NULL; call get_animset() to initialize

	'These are private! Constructor should be called only by frame_load or spriteset_for_frame
	declare constructor(frameset as Frame ptr)
	declare destructor()

	declare function reference() as SpriteSet ptr
	'Recommended to call the spriteset_unload() wrapper instead, to zero out the pointer
	declare sub dereference()

	declare function get_animset() as AnimationSet ptr
	declare function num_frames() as integer
	declare function last_frame_group() as integer
	declare function frame_starts_group(frameidx as integer) as bool
	declare function describe() as string
End Type

declare function spriteset_load(ptno as SpriteType, record as integer) as SpriteSet ptr
declare sub spriteset_unload(ss as SpriteSet ptr ptr)
declare function spriteset_for_frame(fr as Frame ptr) as SpriteSet ptr
declare sub copy_spriteset_data(to_sprite as Frame ptr, from_sprite as Frame ptr)
declare sub copy_spriteset_frameids(to_sprite as Frame ptr, from_sprite as Frame ptr)
declare function spriteset_load_global_animations(sprtype as SpriteType, rgfxdoc as Reload.DocPtr = NULL) as AnimationSet ptr

declare function frame_array_to_vector(frames as Frame ptr) as Frame ptr vector
declare function frame_vector_to_array(frames as Frame ptr vector) as Frame ptr


'==========================================================================================
'                                        Platforms

declare sub show_virtual_keyboard()
declare sub hide_virtual_keyboard()
declare sub show_virtual_gamepad()
declare sub hide_virtual_gamepad()

declare sub remap_android_gamepad(player as integer, gp as GamePadMap)
declare sub remap_touchscreen_button (button_id as integer, ohr_scancode as integer)

declare function read_environment_key(key as string) as string
declare function running_on_console() as bool
declare function running_on_mobile() as bool
declare function running_on_ouya() as bool 'Only use this for things that strictly require OUYA, like the OUYA store
declare function running_on_web() as bool

declare sub ouya_purchase_request (dev_id as string, identifier as string, key_der as string)
declare function ouya_purchase_is_ready () as bool
declare function ouya_purchase_succeeded () as bool

declare sub ouya_receipts_request (dev_id as string, key_der as string)
declare function ouya_receipts_are_ready () as bool
declare function ouya_receipts_result () as string

declare function get_safe_zone_margin () as integer
declare sub set_safe_zone_margin (margin as integer)
declare function supports_safe_zone_margin () as bool

declare sub email_files(address as string, subject as string, message as string, file1 as zstring ptr = NULL, file2 as zstring ptr = NULL, file3 as zstring ptr = NULL)


'==========================================================================================



'Use these macros to avoid deadlocking on gfxmutex due to reentering allmodex from an
'exception handler, if it's already held by the main thread. Not used by the polling thread.
#macro GFX_ENTER
	if main_thread_in_gfx_backend then
		debugc errBug, "GFX_ENTER reentered!"
		'Try to recover by skipping whatever we were about to do
		goto skip_gfx
	end if
	main_thread_in_gfx_backend = YES
	mutexlock gfxmutex
	'Another thread may have just flipped this back.
	'(If there were 3 threads a deadlock would still be possible, but unlikely?)
	main_thread_in_gfx_backend = YES
#endmacro
#macro GFX_EXIT
	mutexunlock gfxmutex
	main_thread_in_gfx_backend = NO
	skip_gfx:
#endmacro


'==========================================================================================
'                                         Globals

extern gfxmutex as any ptr
extern main_thread_in_gfx_backend as bool
extern modex_initialised as bool
extern nogfx_mode as bool
extern faded_in as bool
extern faded_to_color as RGBcolor
extern vpages() as Frame ptr
extern vpagesp as Frame ptr ptr
extern key2text(3,53) as zstring*2
extern fonts() as Font ptr
extern global_tog as integer
extern max_display_fps as integer
extern requested_framerate as double
extern use_speed_control as bool
extern user_toggled_fullscreen as bool
extern debugging_io as bool
extern active_seconds as double
extern idle_time_threshold as double
extern joysticks_globally_disabled as bool

extern as MultiTimer main_timer, gfx_op_timer, gfx_slice_timer

extern "C"
'WARNING: don't pass curmasterpal() to functions from code outside allmodex.bas!
'That will crash because it's not a dynamic array! (FB bug: in other modules, an
'invalid blank temp array descriptor is created and passed.)
extern curmasterpal(256) as RGBcolor
end extern

extern builtin_animations(32) as AnimVariantInfo
extern builtin_anim_variants(32) as AnimVariantInfo

#ENDIF
