'OHRRPGCE - SDL_RWops Lump wrapper
'(C) Copyright 1997-2020 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.

'Used by music_sdl (not music_sdl2) to workaround some SDL_mixer 1.2 bugs, but could be used with other SDL libraries

#include "config.bi"

#ifdef __FB_WIN32__
	'In FB >= 1.04 SDL.bi includes windows.bi; we have to include it first to do the necessary conflict prevention
	include_windows_bi()
#endif

#include "SDL/SDL.bi"

#include "lumpfile.bi"
#include "vector.bi"
#include "common_base.bi"  'debug

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

/' Not used yet

#define FWptr(context)  cast(FileWrapper ptr, context->hidden.unknown.data1)

function lumprwops_seek cdecl (byval context as SDL_RWops ptr, byval offset as int32, byval whence as int32) as int32
	return FileWrapper_seek(*FWptr(context), offset, whence)
end function

function lumprwops_read cdecl (byval context as SDL_RWops ptr, byval bufr as any ptr, byval size as int32, byval maxnum as int32) as int32
	return FileWrapper_read(*FWptr(context), bufr, size, maxnum)
end function

function lumprwops_close cdecl (byval context as SDL_RWops ptr) as int32
	if context then
		FileWrapper_close(*FWptr(context))
		SDL_FreeRW(context)
	end if
	return 0
end function

function SDL_RWFromLump(byval lump as Lump ptr) as SDL_RWops ptr
	dim rw as SDL_RWops ptr
	rw = SDL_AllocRW()
	if rw = NULL then return NULL
	with *rw
		.seek = @lumprwops_seek
		.read = @lumprwops_read
		.write = NULL
		.close = @lumprwops_close
		'it appears that .type is never used, and contains garbage
		.hidden.unknown.data1 = FileWrapper_open(lump)
	end with
	return rw
end function
'/

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

type FnRWopsClose as function cdecl(byval as SDL_RWops ptr) as integer

'vectors of all the safe RWops that are not yet closed, and the corresponding close functions.
dim shared live_RWops as any ptr vector
dim shared live_RWops_closefuncs as any ptr vector

' Initialise globals. But don't a module constructor (module-level code) because
' it might get run before vector.bas's module constructor which initialises the
' 'any ptr vector' type table.
local sub sdl_lumprwops_constructor ()
	v_new live_RWops
	v_new live_RWops_closefuncs
end sub

' On the other hand no ordering problems here
sub sdl_lumprwops_destructor () destructor
	v_free live_RWops
	v_free live_RWops_closefuncs
end sub

'The intent of this function is remove the RWops from live_RWops when it is closed:
'actually calling SDL_RWclose twice is definitely an error
local function safe_RW_close_wrap cdecl (byval context as SDL_RWops ptr) as int32
	dim num as integer = v_find(live_RWops, context)
	if num > -1 then
		'It is live, close it
		dim ret as int32
		ret = cast(FnRWopsClose, live_RWops_closefuncs[num])(context)
		v_delete_slice live_RWops, num, num + 1
		v_delete_slice live_RWops_closefuncs, num, num + 1
		return ret  'I don't know what this signifies
	else
		debug "caught double-close of safe_SDL_RWops: maybe failed to use safe_RW_close?"
	end if
end function

'Wrap an SDL_RWops, overriding the close function. Can be closed/deleted just once,
'can be safely checked for liveness
function safe_RWops (byval rw as SDL_RWops ptr) as SDL_RWops ptr
	if live_RWops = NULL then sdl_lumprwops_constructor
	v_append live_RWops, rw
	v_append live_RWops_closefuncs, cast(any ptr, rw->close)
	rw->close = @safe_RW_close_wrap
	return rw
end function

'After calling 'safe_RWops' on an SDL_RWops, can use this to safely
'close it if it has not already been
sub safe_RWops_close (byval rw as SDL_RWops ptr)
	dim num as integer = v_find(live_RWops, rw)
	if num = -1 then exit sub
	SDL_RWclose(cast(SDL_RWops ptr, live_RWops[num]))
end sub