|
| 1 | +"""Low-level ctypes declarations shared by the public wrappers.""" |
| 2 | +import ctypes |
| 3 | +import ctypes.util |
| 4 | +import os |
| 5 | +import platform |
| 6 | + |
| 7 | +BINK_OK, BINK_FAIL, BINK_FAIL_NULL_POINTER, BINK_FAIL_INVALID_UTF8, BINK_FAIL_NUL_BYTE, BINK_FAIL_PANIC, BINK_FAIL_INVALID_ARGUMENT = range(7) |
| 8 | +BINK_ERROR_WARNING, BINK_ERROR_ERROR = range(2) |
| 9 | +BINK_VALUE_BOOL, BINK_VALUE_INT, BINK_VALUE_FLOAT, BINK_VALUE_STRING, BINK_VALUE_LIST, BINK_VALUE_DIVERT_TARGET, BINK_VALUE_VARIABLE_POINTER = range(7) |
| 10 | + |
| 11 | + |
| 12 | +def _load_library(): |
| 13 | + names = {"Windows": "bink.dll", "Darwin": "libbink.dylib"} |
| 14 | + library_name = names.get(platform.system(), "libbink.so") |
| 15 | + arch = "arm64" if platform.machine() in ("arm64", "aarch64") else "x86_64" |
| 16 | + filename = os.path.join(os.path.dirname(__file__), "native", arch, library_name) |
| 17 | + if not os.path.exists(filename): |
| 18 | + filename = ctypes.util.find_library("bink") or library_name |
| 19 | + try: |
| 20 | + return ctypes.CDLL(filename) |
| 21 | + except (OSError, TypeError) as exc: |
| 22 | + raise RuntimeError("bink library not found") from exc |
| 23 | + |
| 24 | + |
| 25 | +LIB = _load_library() |
| 26 | +P = ctypes.c_void_p |
| 27 | +CP = ctypes.POINTER(ctypes.c_char_p) |
| 28 | +SZ = ctypes.c_size_t |
| 29 | + |
| 30 | + |
| 31 | +def _declare(name, args, restype=ctypes.c_int): |
| 32 | + fn = getattr(LIB, name) |
| 33 | + fn.argtypes, fn.restype = args, restype |
| 34 | + |
| 35 | + |
| 36 | +for _name, _args in { |
| 37 | + "bink_story_new": [ctypes.POINTER(P), ctypes.c_char_p, CP], |
| 38 | + "bink_story_can_continue": [P, ctypes.POINTER(ctypes.c_bool), CP], |
| 39 | + "bink_story_cont": [P, CP, CP], "bink_story_continue_maximally": [P, CP, CP], |
| 40 | + "bink_story_continue_async": [P, ctypes.c_float, ctypes.POINTER(ctypes.c_bool), CP], |
| 41 | + "bink_story_get_current_text": [P, CP, CP], |
| 42 | + "bink_story_get_current_choices": [P, ctypes.POINTER(P), ctypes.POINTER(SZ), CP], |
| 43 | + "bink_story_choose_choice_index": [P, SZ, CP], |
| 44 | + "bink_story_get_current_tags": [P, ctypes.POINTER(P), ctypes.POINTER(SZ), CP], |
| 45 | + "bink_story_get_global_tags": [P, ctypes.POINTER(P), ctypes.POINTER(SZ), CP], |
| 46 | + "bink_story_get_tags_for_content_at_path": [P, ctypes.c_char_p, ctypes.POINTER(P), ctypes.POINTER(SZ), CP], |
| 47 | + "bink_story_choose_path_string": [P, ctypes.c_char_p, CP], |
| 48 | + "bink_story_choose_path_string_with_args": [P, ctypes.c_char_p, ctypes.c_bool, P, CP], |
| 49 | + "bink_story_evaluate_function": [P, ctypes.c_char_p, P, ctypes.POINTER(P), CP, CP], |
| 50 | + "bink_story_load_state": [P, ctypes.c_char_p, CP], "bink_story_save_state": [P, CP, CP], |
| 51 | + "bink_story_reset_state": [P, CP], |
| 52 | + "bink_story_get_visit_count_at_path_string": [P, ctypes.c_char_p, ctypes.POINTER(ctypes.c_int32), CP], |
| 53 | + "bink_story_get_current_path": [P, CP, CP], "bink_story_build_string_of_hierarchy": [P, CP, CP], |
| 54 | + "bink_choices_get_text": [P, SZ, CP, CP], "bink_choices_get_tags": [P, SZ, ctypes.POINTER(P), ctypes.POINTER(SZ), CP], |
| 55 | + "bink_tags_get": [P, SZ, CP, CP], |
| 56 | + "bink_value_new_bool": [ctypes.c_bool, ctypes.POINTER(P), CP], "bink_value_new_int": [ctypes.c_int32, ctypes.POINTER(P), CP], |
| 57 | + "bink_value_new_float": [ctypes.c_float, ctypes.POINTER(P), CP], "bink_value_new_string": [ctypes.c_char_p, ctypes.POINTER(P), CP], |
| 58 | + "bink_value_get_bool": [P, ctypes.POINTER(ctypes.c_bool), CP], "bink_value_get_int": [P, ctypes.POINTER(ctypes.c_int32), CP], |
| 59 | + "bink_value_get_float": [P, ctypes.POINTER(ctypes.c_float), CP], "bink_value_get_string": [P, CP, CP], |
| 60 | + "bink_value_get_kind": [P, ctypes.POINTER(ctypes.c_int), CP], |
| 61 | + "bink_value_array_new": [ctypes.POINTER(P), CP], "bink_value_array_push": [P, P, CP], |
| 62 | + "bink_list_new": [ctypes.POINTER(P), CP], "bink_story_list_new_from_origin": [P, ctypes.c_char_p, ctypes.POINTER(P), CP], |
| 63 | + "bink_story_list_new_from_item": [P, ctypes.c_char_p, ctypes.POINTER(P), CP], "bink_list_add_item": [P, ctypes.c_char_p, ctypes.c_int32, CP], |
| 64 | + "bink_list_get_count": [P, ctypes.POINTER(SZ), CP], "bink_list_get_item": [P, SZ, CP, ctypes.POINTER(ctypes.c_int32), CP], |
| 65 | + "bink_list_get_origin_count": [P, ctypes.POINTER(SZ), CP], "bink_list_get_origin": [P, SZ, CP, CP], |
| 66 | + "bink_value_new_list": [P, ctypes.POINTER(P), CP], "bink_value_get_list": [P, ctypes.POINTER(P), CP], |
| 67 | + "bink_var_get": [P, ctypes.c_char_p, ctypes.POINTER(P), CP], "bink_var_set": [P, ctypes.c_char_p, P, CP], |
| 68 | + "bink_story_switch_flow": [P, ctypes.c_char_p, CP], "bink_story_remove_flow": [P, ctypes.c_char_p, CP], |
| 69 | + "bink_story_switch_to_default_flow": [P, CP], "bink_story_set_allow_external_function_fallbacks": [P, ctypes.c_bool, CP], |
| 70 | + "bink_unbind_external_function": [P, ctypes.c_char_p, CP], |
| 71 | + "bink_fun_args_count": [P, ctypes.POINTER(SZ), CP], "bink_fun_args_get": [P, SZ, ctypes.POINTER(P), CP], |
| 72 | +}.items(): |
| 73 | + _declare(_name, _args) |
| 74 | + |
| 75 | +for _name, _args in {"bink_story_free": [P], "bink_choices_free": [P], "bink_tags_free": [P], "bink_value_free": [P], "bink_value_array_free": [P], "bink_list_free": [P], "bink_cstring_free": [ctypes.c_char_p]}.items(): |
| 76 | + _declare(_name, _args, None) |
| 77 | + |
| 78 | + |
| 79 | +def check(result, error): |
| 80 | + if result == BINK_OK: |
| 81 | + return |
| 82 | + message = error.value.decode("utf-8") if error.value else "Blade Ink FFI error" |
| 83 | + if error.value: |
| 84 | + LIB.bink_cstring_free(error) |
| 85 | + raise RuntimeError(message) |
| 86 | + |
| 87 | + |
| 88 | +def call(name, *args): |
| 89 | + error = ctypes.c_char_p() |
| 90 | + check(getattr(LIB, name)(*args, ctypes.byref(error)), error) |
| 91 | + |
| 92 | + |
| 93 | +def take_string(value): |
| 94 | + try: |
| 95 | + return value.value.decode("utf-8") if value.value else "" |
| 96 | + finally: |
| 97 | + if value.value: |
| 98 | + LIB.bink_cstring_free(value) |
0 commit comments