1import io 2import sys 3from ctypes import Structure, POINTER, CFUNCTYPE, c_int, c_size_t, c_void_p, \ 4 c_char_p, memmove, string_at, Union, _Pointer 5from .dll import _bind, version 6from .stdinc import Sint64, Uint8, Uint16, Uint32, Uint64, SDL_bool 7 8__all__ = [ 9 # Structs 10 "SDL_RWops", 11 12 # Defines 13 "SDL_RWOPS_UNKNOWN", "SDL_RWOPS_WINFILE", "SDL_RWOPS_STDFILE", 14 "SDL_RWOPS_JNIFILE", "SDL_RWOPS_MEMORY", "SDL_RWOPS_MEMORY_RO", 15 "RW_SEEK_SET", "RW_SEEK_CUR", "RW_SEEK_END", 16 17 # Functions 18 "SDL_RWFromFile", "SDL_RWFromFP", "SDL_RWFromMem", "SDL_RWFromConstMem", 19 "SDL_AllocRW", "SDL_FreeRW", "SDL_RWsize", "SDL_RWseek", 20 "SDL_RWtell", "SDL_RWread", "SDL_RWwrite", "SDL_RWclose", 21 "SDL_LoadFile_RW", "SDL_LoadFile", 22 "SDL_ReadU8", "SDL_ReadLE16", "SDL_ReadBE16", "SDL_ReadLE32", 23 "SDL_ReadBE32", "SDL_ReadLE64", "SDL_ReadBE64", "SDL_WriteU8", 24 "SDL_WriteLE16", "SDL_WriteBE16", "SDL_WriteLE32", "SDL_WriteBE32", 25 "SDL_WriteLE64", "SDL_WriteBE64", 26 27 # Python Functions 28 "rw_from_object" 29] 30 31SDL_RWOPS_UNKNOWN = 0 32SDL_RWOPS_WINFILE = 1 33SDL_RWOPS_STDFILE = 2 34SDL_RWOPS_JNIFILE = 3 35SDL_RWOPS_MEMORY = 4 36SDL_RWOPS_MEMORY_RO = 5 37 38class SDL_RWops(Structure): 39 pass 40 41class _hidden(Union): 42 pass 43 44_sdlsize = CFUNCTYPE(Sint64, POINTER(SDL_RWops)) 45_sdlseek = CFUNCTYPE(Sint64, POINTER(SDL_RWops), Sint64, c_int) 46_sdlread = CFUNCTYPE(c_size_t, POINTER(SDL_RWops), c_void_p, c_size_t, c_size_t) 47_sdlwrite = CFUNCTYPE(c_size_t, POINTER(SDL_RWops), c_void_p, c_size_t, c_size_t) 48_sdlclose = CFUNCTYPE(c_int, POINTER(SDL_RWops)) 49SDL_RWops._fields_ = [("size", _sdlsize), 50 ("seek", _sdlseek), 51 ("read", _sdlread), 52 ("write", _sdlwrite), 53 ("close", _sdlclose), 54 ("type", Uint32), 55 ("hidden", _hidden) 56 ] 57 58SDL_RWFromFile = _bind("SDL_RWFromFile", [c_char_p, c_char_p], POINTER(SDL_RWops)) 59SDL_RWFromFP = _bind("SDL_RWFromFP", [c_void_p, SDL_bool], POINTER(SDL_RWops)) 60SDL_RWFromMem = _bind("SDL_RWFromMem", [c_void_p, c_int], POINTER(SDL_RWops)) 61SDL_RWFromConstMem = _bind("SDL_RWFromConstMem", [c_void_p, c_int], POINTER(SDL_RWops)) 62SDL_AllocRW = _bind("SDL_AllocRW", None, POINTER(SDL_RWops)) 63SDL_FreeRW = _bind("SDL_FreeRW", [POINTER(SDL_RWops)]) 64 65SDL_LoadFile_RW = _bind("SDL_LoadFile_RW", [POINTER(SDL_RWops), POINTER(c_size_t), c_int], c_void_p, added='2.0.6') 66# SDL_LoadFile was a macro in SDL <= 2.0.9, added as a function in 2.0.10 67if version >= 2010: 68 SDL_LoadFile = _bind("SDL_LoadFile", [c_char_p, c_size_t], c_void_p) 69else: 70 SDL_LoadFile = lambda fname, ds: SDL_LoadFile_RW(SDL_RWFromFile(fname, "rb"), ds, 1) 71 72RW_SEEK_SET = 0 73RW_SEEK_CUR = 1 74RW_SEEK_END = 2 75 76def _ptr2obj(ptr): 77 """If a pointer, returns its contents. Otherwise, returns the passed object. 78 """ 79 if isinstance(ptr, _Pointer): 80 return ptr.contents 81 return ptr 82 83# The following set of functions were macros in SDL <= 2.0.9 but became full 84# functions in SDL 2.0.10. Lambda functions are to mimic macro behaviour with 85# earlier SDL2 versions. 86if version >= 2010: 87 SDL_RWsize = _bind("SDL_RWsize", [POINTER(SDL_RWops)], Sint64) 88 SDL_RWseek = _bind("SDL_RWseek", [POINTER(SDL_RWops), Sint64, c_int], Sint64) 89 SDL_RWtell = _bind("SDL_RWtell", [POINTER(SDL_RWops)], Sint64) 90 SDL_RWread = _bind("SDL_RWread", [POINTER(SDL_RWops), c_void_p, c_size_t, c_size_t], c_size_t) 91 SDL_RWwrite = _bind("SDL_RWwrite", [POINTER(SDL_RWops), c_void_p, c_size_t, c_size_t], c_size_t) 92 SDL_RWclose = _bind("SDL_RWclose", [POINTER(SDL_RWops)], c_int) 93else: 94 _p = _ptr2obj # allow pointers to be passed directly to these functions 95 SDL_RWsize = lambda ctx: _p(ctx).size(_p(ctx)) 96 SDL_RWseek = lambda ctx, offset, whence: _p(ctx).seek(_p(ctx), offset, whence) 97 SDL_RWtell = lambda ctx: _p(ctx).seek(_p(ctx), 0, RW_SEEK_CUR) 98 SDL_RWread = lambda ctx, ptr, size, n: _p(ctx).read(_p(ctx), ptr, size, n) 99 SDL_RWwrite = lambda ctx, ptr, size, n: _p(ctx).write(_p(ctx), ptr, size, n) 100 SDL_RWclose = lambda ctx: _p(ctx).close(_p(ctx)) 101 102SDL_ReadU8 = _bind("SDL_ReadU8", [POINTER(SDL_RWops)], Uint8) 103SDL_ReadLE16 = _bind("SDL_ReadLE16", [POINTER(SDL_RWops)], Uint16) 104SDL_ReadBE16 = _bind("SDL_ReadBE16", [POINTER(SDL_RWops)], Uint16) 105SDL_ReadLE32 = _bind("SDL_ReadLE32", [POINTER(SDL_RWops)], Uint32) 106SDL_ReadBE32 = _bind("SDL_ReadBE32", [POINTER(SDL_RWops)], Uint32) 107SDL_ReadLE64 = _bind("SDL_ReadLE64", [POINTER(SDL_RWops)], Uint64) 108SDL_ReadBE64 = _bind("SDL_ReadBE64", [POINTER(SDL_RWops)], Uint64) 109 110SDL_WriteU8 = _bind("SDL_WriteU8", [POINTER(SDL_RWops), Uint8], c_size_t) 111SDL_WriteLE16 = _bind("SDL_WriteLE16", [POINTER(SDL_RWops), Uint16], c_size_t) 112SDL_WriteBE16 = _bind("SDL_WriteBE16", [POINTER(SDL_RWops), Uint16], c_size_t) 113SDL_WriteLE32 = _bind("SDL_WriteLE32", [POINTER(SDL_RWops), Uint32], c_size_t) 114SDL_WriteBE32 = _bind("SDL_WriteBE32", [POINTER(SDL_RWops), Uint32], c_size_t) 115SDL_WriteLE64 = _bind("SDL_WriteLE64", [POINTER(SDL_RWops), Uint64], c_size_t) 116SDL_WriteBE64 = _bind("SDL_WriteBE64", [POINTER(SDL_RWops), Uint64], c_size_t) 117 118if sys.version_info[0] >= 3: 119 try: 120 from collections.abc import Callable 121 except ImportError: 122 from collections import Callable 123 callable = lambda x: isinstance(x, Callable) 124 125def rw_from_object(obj): 126 """Creats a SDL_RWops from any Python object. 127 128 The Python object must at least support the following methods: 129 130 read(length) -> data 131 length is the size in bytes to be read. A call to len(data) must 132 return the correct amount of bytes for the data, so that 133 len(data) / [size in bytes for a single element from data] returns 134 the amount of elements. 135 Must raise an error on failure. 136 137 seek(offset, whence) -> int 138 offset denotes the offset to move the read/write pointer of the 139 object to. whence indicates the movement behaviour and can be one 140 of the following values: 141 RW_SEEK_SET - move to offset from the start of the file 142 RW_SEEK_CUR - move by offset from the relative location 143 RW_SEEK_END - move to offset from the end of the file 144 If it could not move read/write pointer to the desired location, 145 an error must be raised. 146 147 tell() -> int 148 Must return the current offset. This method must only be 149 provided, if seek() does not return any value. 150 151 close() -> None 152 Closes the object(or its internal data access methods). Must raise 153 an error on failure. 154 155 write(data) -> None 156 Writes the passed data(which is a string of bytes) to the object. 157 Must raise an error on failure. 158 159 Note: The write() method is optional and only necessary, if the passed 160 object should be able to write data. 161 162 The returned SDL_RWops is a pure Python object and must not be freed via 163 free_rw(). 164 """ 165 if not hasattr(obj, "read"): 166 raise TypeError("obj must have a read(len) -> data method") 167 if not hasattr(obj, "seek") or not callable(obj.seek): 168 raise TypeError("obj must have a seek(offset, whence) method") 169 if not hasattr(obj, "close") or not callable(obj.close): 170 raise TypeError("obj must have a close() -> int method") 171 172 rwops = SDL_RWops() 173 174 def _rwsize(context): 175 try: 176 if hasattr(obj, "size"): 177 if callable(obj.size): 178 return obj.size() 179 else: 180 return obj.size 181 else: 182 cur = obj.seek(0, RW_SEEK_CUR) 183 length = obj.seek(0, RW_SEEK_END) 184 obj.seek(cur, RW_SEEK_CUR) 185 return length 186 except Exception: 187 #print(e) 188 return -1 189 rwops.size = _sdlsize(_rwsize) 190 191 def _rwseek(context, offset, whence): 192 try: 193 retval = obj.seek(offset, whence) 194 if retval is None: 195 retval = obj.tell() 196 return retval 197 except Exception: 198 #print(e) 199 return -1 200 rwops.seek = _sdlseek(_rwseek) 201 202 def _rwread(context, ptr, size, maxnum): 203 try: 204 data = obj.read(size * maxnum) 205 num = len(data) 206 memmove(ptr, data, num) 207 return num // size 208 except Exception: 209 #print(e) 210 return 0 211 rwops.read = _sdlread(_rwread) 212 213 def _rwclose(context): 214 try: 215 retval = obj.close() 216 if retval is None: 217 # No return value; we assume that everything is okay. 218 return 0 219 return retval 220 except Exception: 221 #print(e) 222 return -1 223 rwops.close = _sdlclose(_rwclose) 224 225 def _rwwrite(context, ptr, size, num): 226 try: 227 # string_at feels wrong, since we access a raw byte buffer... 228 retval = obj.write(string_at(ptr, size * num)) 229 if issubclass(type(obj), io.IOBase): 230 if retval is None: # Means write error 231 return 0 232 return retval // size 233 # If not an io object, try to interpret retval as bytes written 234 # and, failing that, just assume success if no exception raised 235 # and return num 236 try: 237 return int(retval) // size 238 except TypeError: 239 return num 240 except Exception: 241 #print(e) 242 return 0 243 244 if hasattr(obj, "write") and callable(obj.write): 245 rwops.write = _sdlwrite(_rwwrite) 246 else: 247 rwops.write = _sdlwrite() 248 return rwops 249