/* * System Interface Library for games * Copyright (c) 2007-2020 Andrew Church * Released under the GNU GPL version 3 or later; NO WARRANTY is provided. * See the file COPYING.txt for details. * * include/SIL/resource.h: Resource management header. */ /* * This file declares the ResourceManager data type and associated * functions, which can be used to manage resources such as images * (textures), sounds, or raw data buffers. ResourceManager alleviates the * need for calling code to separately manage data pointers for each * resource, handles reference counting when a resource is used by multiple * callers, and allows related resources to be freed as a group to help * avoid memory leaks and fragmentation. * * ======== Managing resources ======== * * ResourceManager instances can be created one of two ways: by calling * resource_create() to dynamically allocate and initialize a new instance, * or by defining a static instance with the DEFINE_STATIC_RESOURCEMANAGER() * macro. Once created, a ResourceManager can manage an arbitrary number * of resources, limited only by available memory. However, if the number * of resources to be managed is known ahead of time, memory use and * fragmentation can be reduced by using a static instance initialized with * that number of resource slots. * * When a ResourceManager instance is no longer needed, it can be destroyed * by calling resource_destroy(), which will free all managed resources as * well as memory used by the ResourceManager instance itself. It is also * possible to free just the resources and return the ResourceManager to an * empty state, by calling resource_free_all(). * * To load a resource from a data file, call the resource_load_*() function * appropriate to the resource type, such as resource_load_data() for raw * data files or resource_load_texture() for textures. These functions * return a "resource ID", which can subsequently be passed to the * appropriate resource_get_*() function to retrieve the resource itself, * or to resource_free() to free the resource when it is no longer needed. * * Resource loading is performed in the background, so even if a call to * one of the resource_load_*() functions succeeds, the resource cannot be * used immediately. Before attempting to use a resource, the caller must * either check with resource_sync() that the resource has been loaded, or * call resource_wait() to explicitly wait for the resource. Note that * these functions do not take resource IDs; instead, they accept a * "synchronization mark" value returned by resource_mark(). When * resource_sync() returns true for a given mark value, or after calling * resource_wait() for a given mark value, all resources loaded via a * resource_load_*() function prior to the associated resource_mark() call * are guaranteed to have either finished loading or failed to load. * * In addition to loading resources from files, it is also possible to * create new resources which will be managed by the ResourceManager, using * the appropriate resource_new_*() or resource_strdup() functions. As * with resource_load_*(), these functions return a resource ID which can * be passed to resource_get_*() to retrieve the actual resource data; * unlike loaded resources, newly-created resources are immediately * available when created and need not be waited for. * * There are also shortcut functions named resource_get_new_*() which * return the resource data directly; for example, resource_get_new_data() * is similar to mem_alloc(), except that the returned data block is * managed. Since these functions do not return the resource ID, it is * not possible to free these resources individually; they can only be * freed as a group, by calling resource_free_all(). These functions are * mainly of use in self-contained environments where a ResourceManager is * tightly coupled with the data it manages. * * Finally, if you already have a resource (such as a texture returned by * another function) and want to manage it using a ResourceManager, the * resource_take_*() functions will accept the resource and manage it like * any other managed resource. Note that attempting to register a resource * in this manner when the resource is already managed by a different * ResourceManager will appear to succeed, but is likely to break in * spectacular fashion when one ResourceManager frees the resource and the * other tries to access it. Use resource_link() instead for such cases * (described below). * * Sometimes it may be necessary to use the same resource in separate parts * of the program for different periods of time. To avoid requiring * explicit cooperation between resource users, the resource_link() * function can be used to create a new resource ID, possibly managed by a * different ResourceManager instance, which references the same resource. * Once such a link has been created, both resource IDs are essentially * equivalent; using resource_get_*() on either ID will return the same * resource, and modifications made to the resource using one ID will be * visible using the other ID. However, the resource itself will not be * deallocated until both resource IDs are freed. (This behavior is * analagous to the behavior of hard links on a Unix filesystem.) Note * that the use of links can lead to memory fragmentation if a link to a * resource is still live when related resources are freed. * * To alleviate both the risk of memory fragmentation described above and * unnecessary complexity in managing linked resources (such as with local * caches of resource IDs), ResourceManager also provides the * resource_link_weak() function to create a "weak link" to a resource. * As long as at least one "strong link" -- the original resource ID for * the resource, or a link created with resource_link() -- exists, weak * links behave exactly like strong links. However, as soon as the last * strong link is removed, the resource itself is freed, and any remaining * weak links become "stale links" which no longer reference any resource; * calling resource_get_*() on such a resource will return an error, and * the only valid operation on the stale link is to free it with * resource_free() (or resource_free_all()). The staleness of a weak link * can be checked with the resource_is_stale() function. * * ======== Using package files ======== * * In addition to loading resource files directly from the host filesystem, * ResourceManager also has the capability to load from "package files" * containing embedded resource data. Such package files are implemented * through "package modules" registered with the ResourceManager core code. * A package module includes methods for initialization and cleanup, * retrieving information about an individual file stored in the package, * and decompressing compressed data loaded from the package; see * resource/package.h for the full specification. * * To make use of a package file, register a package module instance using * resource_register_package(). Any subsequent load operations will first * check whether the requested file exists in the package, and will only * attempt to load from the host filesystem if the file is not found in the * package. Package module instances can be unregistered using * resource_unregister_package(), which restores the original behavior for * load operations. In both cases, resources already loaded are not * affected, though unregistering a package file while resources are being * loaded from it may cause the program to crash. * * Multiple package modules can be registered at once. If two or more * packages expose a file with the same name, the one in the most recently * registered package will take precedence. This can be useful when * registering a "patch" package which overlays an existing package file: * load operations will first search the patch package, and if the file is * not found there, the operation will fall back to the original package. * * To avoid name conflicts when they are not desired, each package module * instance can define a prefix at which its filenames will be rooted. * For example, if a package uses the prefix "package:", then the resource * name "package:file.dat" will look for the file named "file.dat" in that * package. * * If a resource in the package file is compressed, compression will be * done synchronously by default, at the time the load is detected to be * complete (which is checked in resource_sync() and resource_wait()). * However, it is also possible to enable background decompression, by * calling resource_set_background_decompression(). When enabled, each * compressed resource will be loaded by a background thread which reads * data one block at a time, decompressing that block and reading the next * one until the entire file has been loaded. Background compression can * be disabled again by calling resource_set_background_decompression() * with all parameters set to zero. * * In addition to potentially saving time through parallel processing, * background decompression also avoids an instantaneous memory spike at * synchronous decompression time when both the entire compressed stream * and the uncompressed data are resident at once. On the flip side, * since reads are performed a block at a time, I/O overhead may lead to * decreased read performance. Because of this, the parameters for * background decompression should be chosen carefully, and may need to be * dynamically adjusted for best performance based on the actual set of * resources being loaded. * * SIL includes one predefined package module in resource/package-pkg.c. * This module handles package files of a custom format, which uses a * simple hashed index for quick access to resource data and also allows * individual data files to be compressed using the "deflate" (gzip) * scheme. The tools/build-pkg.c program can be used to create such * package files. At runtime, use pkg_create_instance() and * pkg_destroy_instance() (declared in resource/package-pkg.h) to create * or destroy instances of the package module. * * ======== Other functionality ======== * * For convenience, SIL also provides functions to check for the existence * of a file (resource_exists()) or list all files under a given directory * (resource_list_files_*()). The latter is a set of three functions * intended to be used in an open-read-close loop, for example: * ResourceFileListHandle *dir = resource_list_files_start("...", 0); * const char *name; * while ((name = resource_list_files_next(dir)) != NULL) {...} * resource_list_files_end(dir); * All four of these functions support package files as well as direct * access to the host filesystem, just like the resource loading functions. * * ======== Resource pathname resolution ======== * * Functions which access data files, including both resource loading * functions and direct-access functions such as resource_exists(), use * the following algorithm for resolving pathnames: * * 1) If the resource name passed to the function begins with a prefix * associated with a package module, that package is used to access the * data. If the name could match two or more package prefixes, the * most recently registered package is used. * * 2) Otherwise, if the resource name begins with "host:", the "host:" is * stripped and the remaining part of the name is used as the pathname. * If the resulting pathname is relative, it will be relative to the * host environment's current working directory. This implies that * resources cannot be given a name starting with "host:", and that * this functionality will be unavailable if a package is registered * with a prefix that is an initial substring of "host:" (including the * empty string). * * 3) Otherwise, if the resource name begins with "/", the name is used * unchanged as the pathname. (This logic is platform-independent; for * example, a path like "c:/windows" is _not_ recognized under this * rule even on Windows systems, and an explicit "host:" prefix is * required to access that path on the host filesystem.) As with the * "host:" prefix, a package whose prefix is the empty string or "/" * will mask this functionality. * * 4) Otherwise, the pathname is constructed by appending the resource name * to the system-dependent resource path prefix: * - On Linux and Windows, this is the directory containing the * executable program. On Linux, if a symbolic link was used to * invoke the program, the link is dereferenced and the directory * of the target (non-symlink) file is used. * - On other platforms, this is the appropriate directory in the * application package. * On Linux, Mac OS X, and Windows, if the preprocessor symbol * SIL_DATA_PATH_ENV_VAR is defined when building and the environment * variable named by that symbol is defined at runtime, the resource * path prefix is overridden with the directory specified by that * environment variable. * * Note that when generating a host filesystem path in step 4, SIL has an * internal limit of 4095 bytes on the final UTF-8 pathname length. This * is not expected to cause any problems on real-world systems as long as * resource names are kept reasonably short (under 1024 bytes). */ #ifndef SIL_RESOURCE_H #define SIL_RESOURCE_H EXTERN_C_BEGIN #ifdef __cplusplus # define private private_ // Avoid errors when included from C++ source. #endif /* External structure declarations. */ struct PackageModuleInfo; struct Sound; struct SysFile; /*************************************************************************/ /****************** Data types and related declarations ******************/ /*************************************************************************/ /* Data type for ResourceManager instances. The majority of the structure * definition is private; two fields are exposed here for the benefit of * the DEFINE_STATIC_RESOURCEMANAGER macro, and the remainder is * encapsulated within the ResourceManagerPrivate structure. */ typedef struct ResourceManager ResourceManager; typedef struct ResourceManagerPrivate ResourceManagerPrivate; struct ResourceManager { /* Pointer to and size (in bytes and in resource records) of the static * buffer reserved for this instance. These are only declared here to * allow setting with DEFINE_STATIC_RESOURCEMANAGER, and they must not * be set or modified externally. */ void * const static_buffer; const int static_size; const int static_count; #ifdef DEBUG /* File and line at which this resource manager was defined. */ const char * const static_file; const int static_line; #endif /* Internal data. */ ResourceManagerPrivate *private; }; /** * DEFINE_STATIC_RESOURCEMANAGER: Define a ResourceManager instance as a * static object, and reserve a buffer for internal data large enough to * hold the given number of resources. A ResourceManager defined in this * way can be used immediately without any initialization. If the number * of resources to be managed exceeds the number passed to this macro, a * dynamically-allocated internal buffer will still be allocated as usual. * * Use this macro as follows: * DEFINE_STATIC_RESOURCEMANAGER(resmgr, num); * where "resmgr" is the identifier to use for the object, and "num" is the * number of resources which the static internal buffer should be able to * hold. For example: * DEFINE_STATIC_RESOURCEMANAGER(my_resmgr, 5); * static int data_resource; * void my_init(void) { * data_resource = resource_load_data(my_resmgr, ...); * } */ /* Note that we don't use designated initializers here because C++ still(!) * doesn't support them, and the macro needs to work from C++ code. */ #define DEFINE_STATIC_RESOURCEMANAGER(resmgr,num) \ static void *resmgr##_static_buffer[ \ _SIL_RESOURCE_SIZE1 + _SIL_RESOURCE_SIZE2*(num)]; \ static ResourceManager resmgr##_static_instance = \ {resmgr##_static_buffer, sizeof(resmgr##_static_buffer), (num), \ _SIL_RESOURCE_DEBUG_FILE_LINE_ NULL}; \ static ResourceManager * const resmgr = &resmgr##_static_instance /* Size constants used by the DEFINE_STATIC_RESOURCEMANAGER() macro. */ #ifdef DEBUG # define _SIL_RESOURCE_SIZE1 (1 + ((140 + sizeof(void *)-1) / sizeof(void *))) #else # define _SIL_RESOURCE_SIZE1 (1 + (( 12 + sizeof(void *)-1) / sizeof(void *))) #endif #define _SIL_RESOURCE_SIZE2 (4 + (( 24 + sizeof(void *)-1) / sizeof(void *)) \ + 1 + /* align up to 64 bits because of int64_t */ \ ((8 - sizeof(void *)) / sizeof(void *))) /* Macro to include the source file and line in a static ResourceManager * definition, but only when debugging. */ #ifdef DEBUG # define _SIL_RESOURCE_DEBUG_FILE_LINE_ __FILE__, __LINE__, #else # define _SIL_RESOURCE_DEBUG_FILE_LINE_ /*nothing*/ #endif /*-----------------------------------------------------------------------*/ /* Handle type used with the resource_list_files_*() functions. The * structure definition is private. */ typedef struct ResourceFileListHandle ResourceFileListHandle; /*-----------------------------------------------------------------------*/ /* Allocation flags for use with resource loading/creation functions. * These follow the same pattern and have the same meanings as the * corresponding MEM_ALLOC_* flags from memory.h, but callers must use * these flags (not MEM_ALLOC_*) when calling ResourceManager functions. */ /* Deliberately skip the MEM_ALLOC_* values (1<<0, 1<<1, 1<<2). */ #define RES_ALLOC_TOP (1<<3) // Allocate from the top of the memory pool. #define RES_ALLOC_TEMP (1<<4) // Allocate from the temporary pool. #define RES_ALLOC_CLEAR (1<<5) // Zero allocated memory (new_data() only). /*************************************************************************/ /******************************* Interface *******************************/ /*************************************************************************/ /*----------------------- Initialization/cleanup ------------------------*/ /** * resource_register_package: Register a package file from which resources * can be loaded. The registration is valid until the package is * unregistered with resource_unregister_package() or resource_cleanup(). * * Depending on the particular module implementing access to the package * file, this function may block. * * [Parameters] * module: Package module instance. * [Return value] * True on success, false on error. */ extern int resource_register_package(struct PackageModuleInfo *module); /** * resource_unregister_package: Unregister a previously registered package * file. This function does nothing if module is NULL or if the given * module instance was not registered. * * [Parameters] * module: Package module instance. * [Return value] * True on success, false on error. */ extern void resource_unregister_package(struct PackageModuleInfo *module); /** * resource_set_background_decompression: Enable or disable background * decompression of compressed resources loaded from a package file, and * configure associated parameters. The new settings are applied from * the next load operation. * * When "on" is true, each time a compressed resource whose compressed data * size is at least "threshold" bytes is loaded, a background thread will * be started to read and decompress the data "buffer_size" bytes at a * time. It naturally makes no sense to specify a threshold value less * than or equal to buffer_size; in that case, the threshold will be set to * buffer_size + 1. (Thus, threshold == 0 is equivalent to saying "all * compressed files larger than buffer_size".) * * The number-of-threads setting only takes effect the first time background * decompression is enabled; attempts to change the number of threads are * ignored. * * By default, background decompression is disabled. * * IMPORTANT: The use of background decompression can cause resource_wait() * to block indefinitely under certain conditions. See the resource_wait() * function documentation for details. * * [Parameters] * on: True to enable background decompression, false to disable. * threshold: Minimum compressed data size for background decompression, * in bytes. * buffer_size: Read buffer size, in bytes. * num_threads: Number of decompression threads to use (must be >= 1). */ extern void resource_set_background_decompression( int on, int threshold, int buffer_size, int num_threads); /*------------ ResourceManager instance creation/destruction ------------*/ /** * resource_create: Create a new, empty ResourceManager instance. * * num_resources is used to set the initial size for internal data * structures used to record information about managed resources. The data * structures will be expanded as necessary, so the value chosen is not * critical; however, if the number of resources to be managed is known in * advance, using an accurate value can help reduce memory waste. * * [Parameters] * num_resources: Initial number of resources to reserve space for. * If zero, a reasonable default value will be used. * [Return value] * Newly created ResourceManager instance, or NULL on error. */ extern ResourceManager *resource_create(int num_resources #ifdef DEBUG , const char *file, int line #endif ); /** * resource_destroy: Free all resources and internal data associated with * the given ResourceManager instance. If the instance was created with * resource_create(), the instance itself is freed, and the instance * pointer is no longer valid after this function returns. If the instance * is a static instance defined with DEFINE_STATIC_RESOURCEMANAGER(), any * dynamically allocated internal buffers are freed, and the instance is * reset to its initial (empty) state. If resmgr == NULL, the function * does nothing. * * This function may block if any resources are being loaded when the * function is called. * * [Parameters] * resmgr: ResourceManager instance to destroy (may be NULL). */ extern void resource_destroy(ResourceManager *resmgr #ifdef DEBUG , const char *file, int line #endif ); /*--------------------- File/directory information ----------------------*/ /** * resource_exists: Return whether a resource with the given name exists. * This is generally faster than attempting to load the resource with * resource_load_*() and checking the result. * * [Parameters] * name: Resource name. * [Return value] * True if the resource exists, false if not. */ extern int resource_exists(const char *name); /** * resource_list_files_start(): Start a directory-list operation on the * given directory path. If the directory belongs to a package file, the * list will include all files within that package file under the given * directory; otherwise, it will include all such files on the host * filesystem. (The list will never include files from more than one * package file or from both a package file and the host filesystem.) * * If "recursive" is true, the directory list will include all files in * subdirectories as well as in the given directory; otherwise, it will * only include files in the given directory, omitting subdirectories. * Note that when reading directly from the host filesystem, this function * imposes a nesting limit of 15 subdirectories in order to avoid the risk * of infinite recursion (such as can result from symbolic links). * * [Parameters] * dir: Directory path for which to obtain a file list. * recursive: True to recursively list files in subdirectories; false * to only list files in the given directory. * [Return value] * File list handle, or NULL on error. */ extern ResourceFileListHandle *resource_list_files_start( const char *dir, int recursive); /** * resource_list_files_next(): Return the next filename for the given * directory list operation. The order of returned files is unspecified. * * The returned pathname is relative to the directory path passed to * resource_list_files_start(). * * The returned string is only valid until the next call to * resource_list_files_next() or resource_list_files_end() on the same * file list handle. * * [Parameters] * handle: File list handle. * [Return value] * Pathname of the next file in the list, or NULL if all files have * been returned. */ extern const char *resource_list_files_next(ResourceFileListHandle *handle); /** * resource_list_files_end(): Close the given file list handle. This * function does nothing if handle == NULL. * * [Parameters] * handle: File list handle. */ extern void resource_list_files_end(ResourceFileListHandle *handle); /*-------------------------- Resource loading ---------------------------*/ /** * resource_load_data: Reserve memory for loading a data resource, and * start the load operation. The resource cannot be used until it has been * loaded and synced with resource_sync() or resource_wait(). * * This function fails if the given resource does not exist. * * [Parameters] * resmgr: ResourceManager instance. * name: Resource name. * align: Memory alignment, in bytes. If zero, the memory is aligned * suitably for any basic data type. * flags: Memory allocation flags (RES_ALLOC_*). * [Return value] * Resource ID (nonzero), or zero on error. */ extern int resource_load_data(ResourceManager *resmgr, const char *name, int align, int flags #ifdef DEBUG , const char *file, int line #endif ); /** * resource_load_texture: Reserve memory for loading a texture resource, * and start the load operation. The resource cannot be used until it has * been loaded and synced with resource_sync() or resource_wait(). * * This function fails if the given resource does not exist. * * [Parameters] * resmgr: ResourceManager instance. * name: Resource name. * flags: Memory allocation flags (RES_ALLOC_*). * mipmaps: True to autogenerate mipmaps. * [Return value] * Resource ID (nonzero), or zero on error. */ extern int resource_load_texture(ResourceManager *resmgr, const char *name, int flags, int mipmaps #ifdef DEBUG , const char *file, int line #endif ); /** * resource_load_bitmap_font: Reserve memory for loading a bitmap font * resource, and start the load operation. The resource cannot be used * until it has been loaded and synced with resource_sync() or * resource_wait(). * * This function fails if the given resource does not exist. * * [Parameters] * resmgr: ResourceManager instance. * name: Resource name. * flags: Memory allocation flags (RES_ALLOC_*). * [Return value] * Resource ID (nonzero), or zero on error. */ extern int resource_load_bitmap_font(ResourceManager *resmgr, const char *name, int flags #ifdef DEBUG , const char *file, int line #endif ); /** * resource_load_freetype_font: Reserve memory for loading a * FreeType-rendered font resource, and start the load operation. The * resource cannot be used until it has been loaded and synced with * resource_sync() or resource_wait(). * * This function fails if the given resource does not exist. * * [Parameters] * resmgr: ResourceManager instance. * name: Resource name. * flags: Memory allocation flags (RES_ALLOC_*). * [Return value] * Resource ID (nonzero), or zero on error. */ extern int resource_load_freetype_font(ResourceManager *resmgr, const char *name, int flags #ifdef DEBUG , const char *file, int line #endif ); /** * resource_load_sound: Reserve memory for loading a sound resource, and * start the load operation. The resource cannot be used until it has been * loaded and synced with resource_sync() or resource_wait(). * * This function fails if the given resource does not exist. * * [Parameters] * resmgr: ResourceManager instance. * name: Resource file name. * flags: Memory allocation flags (RES_ALLOC_*). * [Return value] * Resource ID (nonzero), or zero on error. */ extern int resource_load_sound(ResourceManager *resmgr, const char *name, int flags #ifdef DEBUG , const char *file, int line #endif ); /** * resource_mark: Register a synchronization mark for use with * resource_sync() or resource_wait(). This function never fails, but its * behavior is undefined if called more than 10,000 times without a * successful call to resource_sync() or resource_wait(). * * [Parameters] * resmgr: ResourceManager instance. * [Return value] * Synchronization mark value (nonzero). */ extern int resource_mark(ResourceManager *resmgr); /** * resource_sync: Return the synchronization status of the given mark * value. If this function returns true, all resources whose loads were * started before the associated call to resource_mark() have completed * loading. * * [Parameters] * resmgr: ResourceManager instance. * mark: Synchronization mark value. * [Return value] * True if all previous resources have completed loading, false if not. */ extern int resource_sync(ResourceManager *resmgr, int mark #ifdef DEBUG , const char *file, int line #endif ); /** * resource_wait: Wait for synchronization at the given mark value. When * this function returns, all resources whose loads were started before the * associated call to resource_mark() have completed loading. * * IMPORTANT: When background decompression is enabled, this function can * block indefinitely if a resource is being decompressed in the background * and the system's asynchronous operation table becomes full due to an * outside cause, such as loading numerous uncompressed resources in a * separate ResourceManager instance. Decompression will resume once the * decompressor is able to start new read operations, but if there is no * separate thread to clear out old read operations, the program will * deadlock. * * The above problem does _not_ occur if the asynchronous operations * originate from the same ResourceManager instance, for example when * loading a mix of compressed and uncompressed resources into a single * ResourceManager. * * [Parameters] * resmgr: ResourceManager instance. * mark: Synchronization mark value. */ extern void resource_wait(ResourceManager *resmgr, int mark #ifdef DEBUG , const char *file, int line #endif ); /*-------------------------- Resource creation --------------------------*/ /** * resource_new_data: Create a new data resource. If size == 0, this * function succeeds, but the pointer returned from resource_get_data() * may not be indirected through (though it will be non-NULL). * * [Parameters] * resmgr: ResourceManager instance. * size: Data size, in bytes. * align: Memory alignment, in bytes. If zero, the memory is aligned * suitably for any basic data type. * flags: Memory allocation flags (RES_ALLOC_*). * [Return value] * Resource ID (nonzero), or zero on error. */ extern int resource_new_data(ResourceManager *resmgr, int size, int align, int flags #ifdef DEBUG , const char *file, int line #endif ); /** * resource_copy_data: Create a new data resource as a copy of an * existing data buffer. If size == 0, this function behaves like * resource_new_data(). * * [Parameters] * resmgr: ResourceManager instance. * data: Data buffer to copy. * size: Data size, in bytes. * align: Memory alignment, in bytes. If zero, the memory is aligned * suitably for any basic data type. * flags: Memory allocation flags (RES_ALLOC_*). * [Return value] * Resource ID (nonzero), or zero on error. */ extern int resource_copy_data(ResourceManager *resmgr, const void *data, int size, int align, int flags #ifdef DEBUG , const char *file, int line #endif ); /** * resource_strdup: Create a new data resource as a copy of a string, * like strdup(). * * [Parameters] * resmgr: ResourceManager instance. * str: String to copy. * flags: Memory allocation flags (RES_ALLOC_*). * [Return value] * Resource ID (nonzero), or zero on error. */ extern int resource_strdup(ResourceManager *resmgr, const char *str, int flags #ifdef DEBUG , const char *file, int line #endif ); /** * resource_new_texture: Create a new texture resource. * * [Parameters] * resmgr: ResourceManager instance. * width: Width of texture to create, in pixels. * height: Height of texture to create, in pixels. * flags: Memory allocation flags (RES_ALLOC_*). * mipmaps: True to autogenerate mipmaps. * [Return value] * Resource ID (nonzero), or zero on error. */ extern int resource_new_texture(ResourceManager *resmgr, int width, int height, int flags, int mipmaps #ifdef DEBUG , const char *file, int line #endif ); /** * resource_new_texture_from_display: Create a new texture resource * containing a copy of data from the display (or currently bound * framebuffer), as for texture_create_from_display(). * * As with texture_create_from_display(), some OpenGL ES systems may be * unable to return texture data even if readable is set to true due to * platform-specific constraints. See the documentation of that function * for details. * * [Parameters] * resmgr: ResourceManager instance. * x, y: Base display coordinates of region to copy, in pixels. * w, h: Size of region to copy, in pixels. * readable: False if the texture is not required to be readable (this * may improve performance if the pixel data will never be read out). * flags: Memory allocation flags (RES_ALLOC_*). * mipmaps: True to autogenerate mipmaps. * [Return value] * Resource ID (nonzero), or zero on error. */ extern int resource_new_texture_from_display( ResourceManager *resmgr, int x, int y, int w, int h, int readable, int flags, int mipmaps #ifdef DEBUG , const char *file, int line #endif ); /*----------------------- Resource data retrieval -----------------------*/ /** * resource_get_data: Return the data pointer and size for the given data * resource. This function fails if the given resource is not a data * resource. * * [Parameters] * resmgr: ResourceManager instance. * id: Resource ID. * size_ret: Pointer to variable to receive the data size, in bytes. * May be NULL if this value is not needed. * [Return value] * Data pointer, or NULL on error. */ extern void *resource_get_data(const ResourceManager *resmgr, int id, int *size_ret); /** * resource_get_new_data: Create a new data resource, and return its data * pointer. Since the resource ID is not returned, the resource can only * be freed with resource_free_all() or resource_destroy(). * * [Parameters] * resmgr: ResourceManager instance. * size: Data size, in bytes. * align: Memory alignment, in bytes. If zero, the memory is aligned * suitably for any basic data type. * flags: Memory allocation flags (RES_ALLOC_*). * [Return value] * Data pointer, or NULL on error. */ extern void *resource_get_new_data(ResourceManager *resmgr, int size, int align, int flags #ifdef DEBUG , const char *file, int line #endif ); /** * resource_get_copy_data: Create a new data resource as a copy of a data * buffer, and return the data pointer. Since the resource ID is not * returned, the resource can only be freed with resource_free_all() or * resource_destroy(). * * [Parameters] * resmgr: ResourceManager instance. * data: Data buffer to copy. * size: Data size, in bytes. * align: Memory alignment, in bytes. If zero, the memory is aligned * suitably for any basic data type. * flags: Memory allocation flags (RES_ALLOC_*). * [Return value] * Data pointer, or NULL on error. */ extern void * resource_get_copy_data(ResourceManager *resmgr, const void *data, int size, int align, int flags #ifdef DEBUG , const char *file, int line #endif ); /** * resource_get_strdup: Create a new data resource as a copy of a string, * and return the data pointer. Since the resource ID is not returned, the * resource can only be freed with resource_free_all() or resource_destroy(). * * [Parameters] * resmgr: ResourceManager instance. * str: String to copy. * flags: Memory allocation flags (RES_ALLOC_*). * [Return value] * String pointer, or NULL on error. */ extern char *resource_get_strdup(ResourceManager *resmgr, const char *str, int flags #ifdef DEBUG , const char *file, int line #endif ); /** * resource_get_texture: Return the texture ID for the given texture * resource. This function fails if the given resource is not a texture * resource. * * [Parameters] * resmgr: ResourceManager instance. * id: Resource ID. * [Return value] * Texture ID, or zero on error. */ extern int resource_get_texture(const ResourceManager *resmgr, int id); /** * resource_get_new_texture: Create a new texture resource, and return its * texture ID. Since the resource ID is not returned, the resource can * only be freed with resource_free_all() or resource_destroy(). * * [Parameters] * resmgr: ResourceManager instance. * width: Width of texture to create, in pixels. * height: Height of texture to create, in pixels. * flags: Memory allocation flags (RES_ALLOC_*). * mipmaps: True to autogenerate mipmaps. * [Return value] * Texture ID, or zero on error. */ extern int resource_get_new_texture(ResourceManager *resmgr, int width, int height, int flags, int mipmaps #ifdef DEBUG , const char *file, int line #endif ); /** * resource_get_new_texture_from_display: Create a new texture resource * containing a copy of data from the display, and return its texture ID. * Since the resource ID is not returned, the resource can only be freed * with resource_free_all() or resource_destroy(). * * [Parameters] * resmgr: ResourceManager instance. * x, y: Base display coordinates of region to copy, in pixels. * w, h: Size of region to copy, in pixels. * readable: False if the texture is not required to be readable (this * may improve performance if the pixel data will never be read out). * flags: Memory allocation flags (RES_ALLOC_*). * mipmaps: True to autogenerate mipmaps. * [Return value] * Texture ID, or zero on error. */ extern int resource_get_new_texture_from_display( ResourceManager *resmgr, int x, int y, int w, int h, int readable, int flags, int mipmaps #ifdef DEBUG , const char *file, int line #endif ); /** * resource_get_font: Return the font ID for the given font resource. * This function fails if the given resource is not a font resource. * * [Parameters] * resmgr: ResourceManager instance. * id: Resource ID. * [Return value] * Font ID, or zero on error. */ extern int resource_get_font(const ResourceManager *resmgr, int id); /** * resource_get_sound: Return the Sound object pointer for the given sound * resource. This function fails if the given resource is not a sound * resource. * * [Parameters] * resmgr: ResourceManager instance. * id: Resource ID. * [Return value] * Sound object, or NULL on error. */ extern struct Sound *resource_get_sound(const ResourceManager *resmgr, int id); /*------------------------ Raw data file access -------------------------*/ /** * resource_open_file: Open a data resource for random access without * loading it into memory. On success, the resource can be immediately * used with resource_read_file() or other raw data file access functions * (there is no need to sync the resource). * * This function fails if the given resource does not exist, or if the * resource is stored compressed in a package file. * * [Parameters] * resmgr: ResourceManager instance. * name: Resource name. * [Return value] * Resource ID (nonzero), or zero on error. */ extern int resource_open_file(ResourceManager *resmgr, const char *name #ifdef DEBUG , const char *file, int line #endif ); /** * resource_get_file_size: Return the size of the given data file * resource, which must have been opened with resource_open_file(). * * This function always succeeds when given a valid resource ID. * * [Parameters] * resmgr: ResourceManager instance. * id: Resource ID. * [Return value] * File size, or zero if a parameter is invalid. */ extern int64_t resource_get_file_size(const ResourceManager *resmgr, int id); /** * resource_set_file_position: Set the data offset into the given data * file resource from which the next call to resource_read_file() will read * data. The resource must have been opened with resource_open_file(). * * This function always succeeds when given a valid resource ID. If the * requested position is less than zero, it is taken as zero; if greater * than the size of the resource, it is taken as the size of the resource. * * [Parameters] * resmgr: ResourceManager instance. * id: Resource ID. * pos: New read position. */ extern void resource_set_file_position(const ResourceManager *resmgr, int id, int64_t pos); /** * resource_get_file_position: Return the current read offset for the * given data file resource, which must have been opened with * resource_open_file(). * * This function always succeeds when given a valid resource ID. * * [Parameters] * resmgr: ResourceManager instance. * id: Resource ID. * [Return value] * File size, or zero if a parameter is invalid. */ extern int64_t resource_get_file_position(const ResourceManager *resmgr, int id); /** * resource_read_file: Read data from the given data file resource, which * must have been opened with resource_open_file(). * * [Parameters] * resmgr: ResourceManager instance. * id: Resource ID. * buf: Buffer into which to read data. * len: Number of bytes to read. * [Return value] * Number of bytes read, or a negative value on error. */ extern int resource_read_file(const ResourceManager *resmgr, int id, void *buf, int len); /** * resource_read_file_at: Read data from a specified position in the given * data file resource, which must have been opened with resource_open_file(). * Calling this function does not change the file position used for reading * with resource_read_file(). * * [Parameters] * resmgr: ResourceManager instance. * id: Resource ID. * buf: Buffer into which to read data. * len: Number of bytes to read. * pos: File position from which to read. * [Return value] * Number of bytes read, or a negative value on error. */ extern int resource_read_file_at(const ResourceManager *resmgr, int id, void *buf, int len, int64_t pos); /** * resource_get_file_handle: Return a low-level file handle for the given * data file resource, which must have been opened with resource_open_file(). * The returned file handle is suitable for returning from a custom package * format's PackageFileInfoFunc implementation in the file_ret parameter; * it should not be used for any other purpose. * * This function always succeeds when given a valid resource ID. * * [Parameters] * resmgr: ResourceManager instance. * id: Resource ID. * offset_ret: Pointer to variable to receive the resource's data offset * within the low-level file. * [Return value] * Low-level file handle, or NULL if a parameter is invalid. */ extern struct SysFile *resource_get_file_handle(const ResourceManager *resmgr, int id, int64_t *offset_ret); /*---------------------- Other resource operations ----------------------*/ /** * resource_open_sound: Open a streaming sound resource. On success, the * Sound object can be immediately retrieved with resource_get_sound() * (there is no need to sync the resource). * * This function fails if the given resource does not exist, or if the * resource is stored compressed in a package file (this only refers to * package file compression, not audio data compression such as MP3). * * [Parameters] * resmgr: ResourceManager instance. * name: Resource name. * [Return value] * Resource ID (nonzero), or zero on error. */ extern int resource_open_sound(ResourceManager *resmgr, const char *name #ifdef DEBUG , const char *file, int line #endif ); /** * resource_open_sound_from_file: Open a streaming sound resource * embedded in a data file. On success, the Sound object can be * immediately retrieved with resource_get_sound() (there is no need to * sync the resource). * * The data file resource must have been opened with resource_open_file(). * * [Parameters] * resmgr: ResourceManager instance in which to create new resource. * file_resmgr: ResourceManager instance containing data file resource. * file_id: ID of data file resource. * offset: Byte offset within data file at which sound resource starts. * size: Size of sound resource data, in bytes. * [Return value] * Resource ID (nonzero), or zero on error. */ extern int resource_open_sound_from_file( ResourceManager *resmgr, ResourceManager *file_resmgr, int file_id, int64_t offset, int size #ifdef DEBUG , const char *file, int line #endif ); /** * resource_take_data: Take ownership of the given data buffer, treating * it as a data resource. The data buffer must have been allocated using * mem_alloc(). * * The value passed in for "size" is only used for returning via * resource_get_data(), and can be set to zero if (for example) the caller * does not know the buffer size, without any ill effects. * * [Parameters] * resmgr: ResourceManager instance. * data: Data buffer to take ownership of. * size: Data size, in bytes. * [Return value] * Resource ID (nonzero), or zero on error. */ extern int resource_take_data(ResourceManager *resmgr, void *data, int size #ifdef DEBUG , const char *file, int line #endif ); /** * resource_take_texture: Take ownership of the given texture, treating it * as a texture resource. * * [Parameters] * resmgr: ResourceManager instance. * texture_id: ID of texture to take ownership of. * [Return value] * Resource ID (nonzero), or zero on error. */ extern int resource_take_texture(ResourceManager *resmgr, int texture_id #ifdef DEBUG , const char *file, int line #endif ); /** * resource_take_sound: Take ownership of the given sound, treating it as * a sound resource. * * [Parameters] * resmgr: ResourceManager instance. * sound: Sound object to take ownership of. * [Return value] * Resource ID (nonzero), or zero on error. */ extern int resource_take_sound(ResourceManager *resmgr, struct Sound *sound #ifdef DEBUG , const char *file, int line #endif ); /** * resource_link: Create a link to the given resource, which may be owned * by a different ResourceManager instance. The resource will not be * destroyed until all links, including the ID under which it was * originally loaded, have been freed with resource_free() or * resource_free_all(). * * [Parameters] * resmgr: ResourceManager instance in which to create the link. * old_resmgr: ResourceManager instance which owns the existing resource. * old_id: Resource ID of the existing resource. * [Return value] * Resource ID of the link (nonzero), or zero on error. */ extern int resource_link(ResourceManager *resmgr, const ResourceManager *old_resmgr, int old_id #ifdef DEBUG , const char *file, int line #endif ); /** * resource_link_weak: Create a weak link to the given resource. Unlike * ordinary ("strong") links, weak links do not pin a resource in memory. * When the last strong link to a resource is freed, the resource is * destroyed; any remaining weak links to that resource become stale, and * attempting to retrieve the resource's data will result in an error. * This error case can be differentiated from other errors by calling * resource_is_stale() on the weak link. * * [Parameters] * resmgr: ResourceManager instance in which to create the link. * old_resmgr: ResourceManager instance which owns the existing resource. * old_id: Resource ID of the existing resource. * [Return value] * Resource ID of the link (nonzero), or zero on error. */ extern int resource_link_weak(ResourceManager *resmgr, const ResourceManager *old_resmgr, int old_id #ifdef DEBUG , const char *file, int line #endif ); /** * resource_is_stale: Return whether the given resource ID is a stale * link. This function returns false for any resource ID which is not a * weak link, including invalid ID values. * * [Parameters] * resmgr: ResourceManager instance. * id: Resource ID. * [Return value] * True if the resource is a stale link, false otherwise. */ extern int resource_is_stale(const ResourceManager *resmgr, int id); /** * resource_free: Free the given resource. The resource data itself is * not destroyed if there are any strong links to the resource remaining. * This function does nothing if id == 0. * * If the given resource is currently being loaded, this function may block * until the load completes. * * [Parameters] * resmgr: ResourceManager instance. * id: Resource ID. */ extern void resource_free(ResourceManager *resmgr, int id #ifdef DEBUG , const char *file, int line #endif ); /** * resource_free_all: Free all resources managed by the given * ResourceManager instance. * * If any resources are currently being loaded, this function may block * until those loads complete. * * [Parameters] * resmgr: ResourceManager instance. */ extern void resource_free_all(ResourceManager *resmgr #ifdef DEBUG , const char *file, int line #endif ); /*----------------- Package module creation/destruction -----------------*/ /** * pkg_create_instance: Create a new package module instance for a * PKG-format package file. * * [Parameters] * package_path: Pathname of the package file. This pathname is * resolved in the same manner as names for resource data files. * prefix: Resource pathname prefix to apply to files in this package. * For example, a prefix of "pkg:" would expose a resource named * "file.dat" as "pkg:file.dat". * [Return value] * Newly created module instance, or NULL on error. */ extern struct PackageModuleInfo *pkg_create_instance( const char *package_path, const char *prefix); /** * pkg_destroy_instance: Destroy a package module instance for a * PKG-format package file. The instance must not be registered with the * resource management routines. * * This function does nothing if module == NULL. * * [Parameters] * module: Module instance to destroy. */ extern void pkg_destroy_instance(struct PackageModuleInfo *module); /*************************************************************************/ /************************** Debugging wrappers ***************************/ /*************************************************************************/ /* * When debugging is enabled, we wrap allocating calls with these macros * which pass down the source file and line at which the call was made. * Without these macros, every resource-related memory allocation would * appear to have been made by resource/core.c, significantly hampering * debugging of resource-related memory issues. */ #ifdef DEBUG # define resource_create(num_resources) \ resource_create((num_resources), __FILE__, __LINE__) # define resource_destroy(resmgr) \ resource_destroy((resmgr), __FILE__, __LINE__) # define resource_load_data(resmgr,name,align,flags) \ resource_load_data((resmgr), (name), (align), (flags), __FILE__, __LINE__) # define resource_load_texture(resmgr,name,flags,mipmaps) \ resource_load_texture((resmgr), (name), (flags), (mipmaps), \ __FILE__, __LINE__) # define resource_load_bitmap_font(resmgr,name,flags) \ resource_load_bitmap_font((resmgr), (name), (flags), __FILE__, __LINE__) # define resource_load_freetype_font(resmgr,name,flags) \ resource_load_freetype_font((resmgr), (name), (flags), __FILE__, __LINE__) # define resource_load_sound(resmgr,name,flags) \ resource_load_sound((resmgr), (name), (flags), __FILE__, __LINE__) # define resource_sync(resmgr,mark) \ resource_sync((resmgr), (mark), __FILE__, __LINE__) # define resource_wait(resmgr,mark) \ resource_wait((resmgr), (mark), __FILE__, __LINE__) # define resource_new_data(resmgr,size,align,flags) \ resource_new_data((resmgr), (size), (align), (flags), __FILE__, __LINE__) # define resource_copy_data(resmgr,data,size,align,flags) \ resource_copy_data((resmgr), (data), (size), (align), (flags), \ __FILE__, __LINE__) # define resource_strdup(resmgr,str,flags) \ resource_strdup((resmgr), (str), (flags), __FILE__, __LINE__) # define resource_new_texture(resmgr,width,height,flags,mipmaps) \ resource_new_texture((resmgr), (width), (height), (flags), (mipmaps), \ __FILE__, __LINE__) # define resource_new_texture_from_display(resmgr,x,y,w,h,readable,flags,mipmaps) \ resource_new_texture_from_display( \ (resmgr), (x), (y), (w), (h), (readable), (flags), (mipmaps), \ __FILE__, __LINE__) # define resource_get_new_data(resmgr,size,align,flags) \ resource_get_new_data((resmgr), (size), (align), (flags), __FILE__, \ __LINE__) # define resource_get_copy_data(resmgr,data,size,align,flags) \ resource_get_copy_data((resmgr), (data), (size), (align), (flags), \ __FILE__, __LINE__) # define resource_get_strdup(resmgr,str,flags) \ resource_get_strdup((resmgr), (str), (flags), __FILE__, __LINE__) # define resource_get_new_texture(resmgr,width,height,flags,mipmaps) \ resource_get_new_texture((resmgr), (width), (height), (flags), (mipmaps), \ __FILE__, __LINE__) # define resource_get_new_texture_from_display(resmgr,x,y,w,h,readable,flags,mipmaps) \ resource_get_new_texture_from_display( \ (resmgr), (x), (y), (w), (h), (readable), (flags), (mipmaps), \ __FILE__, __LINE__) # define resource_open_file(resmgr,name) \ resource_open_file((resmgr), (name), __FILE__, __LINE__) # define resource_open_sound(resmgr,name) \ resource_open_sound((resmgr), (name), __FILE__, __LINE__) # define resource_open_sound_from_file(resmgr,file_resmgr,file_id,offset,size) \ resource_open_sound_from_file((resmgr), (file_resmgr), (file_id), \ (offset), (size), __FILE__, __LINE__) # define resource_take_data(resmgr,data,size) \ resource_take_data((resmgr), (data), (size), __FILE__, __LINE__) # define resource_take_texture(resmgr,texture_id) \ resource_take_texture((resmgr), (texture_id), __FILE__, __LINE__) # define resource_take_sound(resmgr,sound) \ resource_take_sound((resmgr), (sound), __FILE__, __LINE__) # define resource_link(resmgr,old_resmgr,old_id) \ resource_link((resmgr), (old_resmgr), (old_id), __FILE__, __LINE__) # define resource_link_weak(resmgr,old_resmgr,old_id) \ resource_link_weak((resmgr), (old_resmgr), (old_id), __FILE__, __LINE__) # define resource_free(resmgr,id) \ resource_free((resmgr), (id), __FILE__, __LINE__) # define resource_free_all(resmgr) \ resource_free_all((resmgr), __FILE__, __LINE__) #endif /*************************************************************************/ /*************************************************************************/ #ifdef __cplusplus # undef private #endif EXTERN_C_END #endif // SIL_RESOURCE_H