From 606d35ba6cc6170f200dde15ba5c839a9c553474 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Fri, 14 Oct 2016 15:30:40 -0700 Subject: [PATCH] Python: Add VFS bindings --- src/platform/python/_builder.h | 5 +- src/platform/python/_builder.py | 11 ++- src/platform/python/mgba/core.py | 18 +++++ src/platform/python/mgba/vfs.py | 126 +++++++++++++++++++++++++++++++ src/platform/python/setup.py.in | 4 +- src/platform/python/vfs-py.c | 46 +++++++++++ src/platform/python/vfs-py.h | 28 +++++++ src/util/vfs.h | 7 +- 8 files changed, 238 insertions(+), 7 deletions(-) create mode 100644 src/platform/python/mgba/vfs.py create mode 100644 src/platform/python/vfs-py.c create mode 100644 src/platform/python/vfs-py.h diff --git a/src/platform/python/_builder.h b/src/platform/python/_builder.h index 777b60c26..ace007289 100644 --- a/src/platform/python/_builder.h +++ b/src/platform/python/_builder.h @@ -1,14 +1,15 @@ #define COMMON_H -#define extern #define _TIME_H_ #define _SYS_TIME_H_ #define ATTRIBUTE_FORMAT(X, Y, Z) #define DECL_BITFIELD(newtype, oldtype) typedef oldtype newtype #define DECL_BIT(type, name, bit) #define DECL_BITS(type, name, bit, nbits) -typedef long time_t; +typedef int... time_t; +typedef int... off_t; typedef ... va_list; #include +#include "platform/python/vfs-py.h" #include "core/core.h" #ifdef M_CORE_GBA #include "arm/arm.h" diff --git a/src/platform/python/_builder.py b/src/platform/python/_builder.py index f1e389d2d..6a59604ae 100644 --- a/src/platform/python/_builder.py +++ b/src/platform/python/_builder.py @@ -13,10 +13,19 @@ ffi.set_source("mgba._pylib", """ #include "gba/gba.h" #include "lr35902/lr35902.h" #include "gb/gb.h" +#include "util/vfs.h" + +struct VFile* VFileFromPython(void* fileobj); + +struct VFilePy { + struct VFile d; + void* fileobj; +}; """, include_dirs=[src], extra_compile_args=sys.argv[1:], libraries=["mgba"], - library_dirs=[os.path.join(os.getcwd(), "..")]) + library_dirs=[os.path.join(os.getcwd(), "..")], + sources=[os.path.join(os.path.dirname(__file__), path) for path in ["vfs-py.c"]]) with open(os.path.join(os.getcwd(), "_builder.h")) as core: lines = [] diff --git a/src/platform/python/mgba/core.py b/src/platform/python/mgba/core.py index 88d08bc09..57c8824fd 100644 --- a/src/platform/python/mgba/core.py +++ b/src/platform/python/mgba/core.py @@ -6,12 +6,24 @@ def find(path): return None return Core(core) +def findVF(vf): + core = lib.mCoreFindVF(vf.handle()) + if core == ffi.NULL: + return None + return Core(core) + def loadPath(path): core = find(path) if not core or not core.loadFile(path): return None return core +def loadVF(vf): + core = findVF(vf) + if not core or not core.loadROM(vf): + return None + return core + class Core: def __init__(self, native): self._core = ffi.gc(native, self._deinit) @@ -32,6 +44,12 @@ class Core: def loadFile(self, path): return bool(lib.mCoreLoadFile(self._core, path.encode('UTF-8'))) + def isROM(self, vf): + return bool(self._core.isROM(vf.handle())) + + def loadROM(self, vf): + return bool(self._core.loadROM(self._core, vf.handle())) + def autoloadSave(self): return bool(lib.mCoreAutoloadSave(self._core)) diff --git a/src/platform/python/mgba/vfs.py b/src/platform/python/mgba/vfs.py new file mode 100644 index 000000000..b73d2004c --- /dev/null +++ b/src/platform/python/mgba/vfs.py @@ -0,0 +1,126 @@ +from _pylib import ffi, lib +import mmap +import os + +@ffi.def_extern() +def _vfpClose(vf): + vfp = ffi.cast("struct VFilePy*", vf) + ffi.from_handle(vfp.fileobj).close() + +@ffi.def_extern() +def _vfpSeek(vf, offset, whence): + vfp = ffi.cast("struct VFilePy*", vf) + f = ffi.from_handle(vfp.fileobj) + f.seek(offset, whence) + return f.tell() + +@ffi.def_extern() +def _vfpRead(vf, buffer, size): + vfp = ffi.cast("struct VFilePy*", vf) + pybuf = ffi.buffer(buffer, size) + return ffi.from_handle(vfp.fileobj).readinto(pybuf) + +@ffi.def_extern() +def _vfpWrite(vf, buffer, size): + vfp = ffi.cast("struct VFilePy*", vf) + pybuf = ffi.buffer(buffer, size) + return ffi.from_handle(vfp.fileobj).write(pybuf) + +@ffi.def_extern() +def _vfpMap(vf, size, flags): + pass + +@ffi.def_extern() +def _vfpUnmap(vf, memory, size): + pass + +@ffi.def_extern() +def _vfpTruncate(vf, size): + vfp = ffi.cast("struct VFilePy*", vf) + ffi.from_handle(vfp.fileobj).truncate(size) + +@ffi.def_extern() +def _vfpSize(vf): + vfp = ffi.cast("struct VFilePy*", vf) + f = ffi.from_handle(vfp.fileobj) + pos = f.tell() + f.seek(0, os.SEEK_END) + size = f.tell() + f.seek(pos, os.SEEK_SET) + return size + +@ffi.def_extern() +def _vfpSync(vf, buffer, size): + vfp = ffi.cast("struct VFilePy*", vf) + f = ffi.from_handle(vfp.fileobj) + if buffer and size: + pos = f.tell() + f.seek(0, os.SEEK_SET) + _vfpWrite(vf, buffer, size) + f.seek(pos, os.SEEK_SET) + f.flush() + os.fsync() + return True + +def open(f): + handle = ffi.new_handle(f) + vf = VFile(lib.VFileFromPython(handle)) + # Prevent garbage collection + vf._fileobj = f + vf._handle = handle + return vf + +def openPath(path, mode="r"): + flags = 0 + if mode.startswith("r"): + flags |= os.O_RDONLY + elif mode.startswith("w"): + flags |= os.O_WRONLY | os.O_CREAT | os.O_TRUNC + elif mode.startswith("a"): + flags |= os.O_WRONLY | os.O_CREAT | os.O_APPEND + else: + return None + + if "+" in mode[1:]: + flags |= os.O_RDWR + if "x" in mode[1:]: + flags |= os.O_EXCL + + return VFile(lib.VFileOpen(path.encode("UTF-8"), flags)) + +class VFile: + def __init__(self, vf): + self._vf = vf + + def handle(self): + return self._vf + + def close(self): + return self._vf.close(self._vf) + + def seek(self, offset, whence): + return self._vf.seek(self._vf, offset, whence) + + def read(self, buffer, size): + return self._vf.read(self._vf, buffer, size) + + def readline(self, buffer, size): + return self._vf.readline(self._vf, buffer, size) + + def write(self, buffer, size): + return self._vf.write(self._vf, buffer, size) + + def map(self, size, flags): + return self._vf.map(self._vf, size, flags) + + def unmap(self, memory, size): + self._vf.unmap(self._vf, memory, size) + + def truncate(self, size): + self._vf.truncate(self._vf, size) + + def size(self): + return self._vf.size(self._vf) + + def sync(self, buffer, size): + return self._vf.sync(self._vf, buffer, size) diff --git a/src/platform/python/setup.py.in b/src/platform/python/setup.py.in index 857c91fd7..133474831 100644 --- a/src/platform/python/setup.py.in +++ b/src/platform/python/setup.py.in @@ -1,4 +1,5 @@ from setuptools import setup +import re classifiers = [ "Programming Language :: Python :: 2", @@ -6,9 +7,8 @@ classifiers = [ "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)" ] - setup(name="${BINARY_NAME}", - version="${VERSION_STRING}", + version=re.sub("/", "-", "${VERSION_STRING}"), author="Jeffrey Pfau", author_email="jeffrey@endrift.com", url="http://github.com/mgba-emu/mgba/", diff --git a/src/platform/python/vfs-py.c b/src/platform/python/vfs-py.c new file mode 100644 index 000000000..51b1f182b --- /dev/null +++ b/src/platform/python/vfs-py.c @@ -0,0 +1,46 @@ +/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "util/vfs.h" + +struct VFilePy { + struct VFile d; + void* fileobj; +}; + +bool _vfpClose(struct VFile* vf); +off_t _vfpSeek(struct VFile* vf, off_t offset, int whence); +ssize_t _vfpRead(struct VFile* vf, void* buffer, size_t size); +ssize_t _vfpWrite(struct VFile* vf, const void* buffer, size_t size); +void* _vfpMap(struct VFile* vf, size_t size, int flags); +void _vfpUnmap(struct VFile* vf, void* memory, size_t size); +void _vfpTruncate(struct VFile* vf, size_t size); +ssize_t _vfpSize(struct VFile* vf); +bool _vfpSync(struct VFile* vf, const void* buffer, size_t size); + +struct VFile* VFileFromPython(void* fileobj) { + if (!fileobj) { + return 0; + } + + struct VFilePy* vfp = malloc(sizeof(struct VFilePy)); + if (!vfp) { + return 0; + } + + vfp->fileobj = fileobj; + vfp->d.close = _vfpClose; + vfp->d.seek = _vfpSeek; + vfp->d.read = _vfpRead; + vfp->d.readline = VFileReadline; + vfp->d.write = _vfpWrite; + vfp->d.map = _vfpMap; + vfp->d.unmap = _vfpUnmap; + vfp->d.truncate = _vfpTruncate; + vfp->d.size = _vfpSize; + vfp->d.sync = _vfpSync; + + return &vfp->d; +} diff --git a/src/platform/python/vfs-py.h b/src/platform/python/vfs-py.h new file mode 100644 index 000000000..82a5ac0cf --- /dev/null +++ b/src/platform/python/vfs-py.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "util/vfs.h" + +struct VFilePy { + struct VFile d; + void* fileobj; +}; + +struct VFile* VFileFromPython(void* fileobj); + +extern "Python+C" { + +bool _vfpClose(struct VFile* vf); +off_t _vfpSeek(struct VFile* vf, off_t offset, int whence); +ssize_t _vfpRead(struct VFile* vf, void* buffer, size_t size); +ssize_t _vfpWrite(struct VFile* vf, const void* buffer, size_t size); +void* _vfpMap(struct VFile* vf, size_t size, int flags); +void _vfpUnmap(struct VFile* vf, void* memory, size_t size); +void _vfpTruncate(struct VFile* vf, size_t size); +ssize_t _vfpSize(struct VFile* vf); +bool _vfpSync(struct VFile* vf, const void* buffer, size_t size); + +} \ No newline at end of file diff --git a/src/util/vfs.h b/src/util/vfs.h index 19c71da92..398372887 100644 --- a/src/util/vfs.h +++ b/src/util/vfs.h @@ -65,12 +65,11 @@ struct VDir { struct VFile* VFileOpen(const char* path, int flags); struct VFile* VFileOpenFD(const char* path, int flags); -struct VFile* VFileFOpen(const char* path, const char* mode); struct VFile* VFileFromFD(int fd); + struct VFile* VFileFromMemory(void* mem, size_t size); struct VFile* VFileFromConstMemory(const void* mem, size_t size); struct VFile* VFileMemChunk(const void* mem, size_t size); -struct VFile* VFileFromFILE(FILE* file); struct VDir* VDirOpen(const char* path); struct VDir* VDirOpenArchive(const char* path); @@ -83,7 +82,11 @@ struct VDir* VDirOpenZip(const char* path, int flags); struct VDir* VDirOpen7z(const char* path, int flags); #endif +#if defined(WII) || defined(_3DS) +struct VFile* VFileFOpen(const char* path, const char* mode); +struct VFile* VFileFromFILE(FILE* file); struct VDir* VDeviceList(void); +#endif void separatePath(const char* path, char* dirname, char* basename, char* extension);