mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-07-09 20:41:29 -04:00
Resource, texture and overlay services
This commit is contained in:
@@ -153,7 +153,7 @@ struct LoadedMod {
|
||||
std::unique_ptr<NativeMod> native;
|
||||
std::unique_ptr<ModContext> context;
|
||||
|
||||
// Shared so in-flight readers keep their bundle alive across disable/reload.
|
||||
// Shared with overlay file registrations so in-flight DVD reads survive disable/reload.
|
||||
std::shared_ptr<ModBundle> bundle;
|
||||
|
||||
ModManifestInfo manifestInfo;
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
#ifndef DUSK_TEXTURE_REPLACEMENTS_HPP
|
||||
#define DUSK_TEXTURE_REPLACEMENTS_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace dusk::texture_replacements {
|
||||
|
||||
// Mod replacements are prioritized *over* user replacements (<data folder>/texture_replacements/)
|
||||
inline constexpr int32_t kUserTextureReplacementPriority = -1'000'000;
|
||||
|
||||
void reload();
|
||||
void set_enabled(bool enabled);
|
||||
void shutdown();
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include "mods/api.h"
|
||||
|
||||
#define OVERLAY_SERVICE_ID "dev.twilitrealm.dusklight.overlay"
|
||||
#define OVERLAY_SERVICE_MAJOR 1u
|
||||
#define OVERLAY_SERVICE_MINOR 0u
|
||||
|
||||
/* Handle for a runtime overlay registration. 0 is never a valid handle. */
|
||||
typedef uint64_t OverlayHandle;
|
||||
|
||||
/*
|
||||
* Runtime DVD file overlays.
|
||||
*
|
||||
* Registrations are owned by the calling mod and removed automatically when it is disabled,
|
||||
* reloaded, or fails. Changes are applied at the next frame boundary; data the game has already
|
||||
* read stays in memory until it re-reads the file (sometimes on scene reload, sometimes on
|
||||
* restart).
|
||||
*
|
||||
* disc_path names the file to overlay: absolute with a leading '/', matched against the disc
|
||||
* case-insensitively (e.g. "/res/Stage/R04_00.arc"). Paths that do not exist on the disc are added
|
||||
* as new files.
|
||||
*
|
||||
* If multiple sources overlay the same path, the last one wins: a mod's runtime registrations beat
|
||||
* its static overlay/ files, and later-loaded mods beat earlier ones.
|
||||
*/
|
||||
typedef struct OverlayService {
|
||||
ServiceHeader header;
|
||||
|
||||
/*
|
||||
* Overlay disc_path with a file from the calling mod's bundle (bundle-relative path, e.g.
|
||||
* "res/replacement.arc"). The file's contents are read lazily on each open, so the bundle
|
||||
* file must not change size while registered.
|
||||
*/
|
||||
ModResult (*add_file)(
|
||||
ModContext* ctx, const char* disc_path, const char* bundle_path, OverlayHandle* out_handle);
|
||||
|
||||
/*
|
||||
* Overlay disc_path with a caller-owned buffer. The data is copied; the caller may free it
|
||||
* as soon as this returns.
|
||||
*/
|
||||
ModResult (*add_buffer)(ModContext* ctx, const char* disc_path, const void* data, size_t size,
|
||||
OverlayHandle* out_handle);
|
||||
|
||||
/* Remove a runtime overlay previously added by the calling mod. */
|
||||
ModResult (*remove)(ModContext* ctx, OverlayHandle handle);
|
||||
} OverlayService;
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "mods/service.hpp"
|
||||
|
||||
template <>
|
||||
struct dusk::mods::ServiceTraits<OverlayService> {
|
||||
static constexpr const char* id = OVERLAY_SERVICE_ID;
|
||||
static constexpr uint16_t major_version = OVERLAY_SERVICE_MAJOR;
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include "mods/api.h"
|
||||
|
||||
/*
|
||||
* Read-only access to the res/ tree of the calling mod's own bundle. Reload serves the new
|
||||
* bundle's contents. For writable storage, use HostService::mod_dir.
|
||||
*/
|
||||
|
||||
#define RESOURCE_SERVICE_ID "dev.twilitrealm.dusklight.resource"
|
||||
#define RESOURCE_SERVICE_MAJOR 1u
|
||||
#define RESOURCE_SERVICE_MINOR 0u
|
||||
|
||||
/*
|
||||
* A loaded resource, allocated by the service. Return every successful load with free;
|
||||
* buffers still live when the mod is disabled or reloaded are reclaimed with a warning.
|
||||
*/
|
||||
typedef struct ResourceBuffer {
|
||||
uint32_t struct_size;
|
||||
void* data;
|
||||
size_t size;
|
||||
} ResourceBuffer;
|
||||
|
||||
#define RESOURCE_BUFFER_INIT {sizeof(ResourceBuffer), NULL, 0u}
|
||||
|
||||
typedef struct ResourceService {
|
||||
ServiceHeader header;
|
||||
|
||||
/*
|
||||
* Load a file into a fresh allocation. `relative_path` is resolved against the bundle's
|
||||
* res/ directory. Absolute paths and ".." are rejected. MOD_UNAVAILABLE if the file does not
|
||||
* exist. An empty file loads as data == NULL with size 0. Previous contents of `out_buffer` are
|
||||
* overwritten, not freed.
|
||||
*/
|
||||
ModResult (*load)(ModContext* ctx, const char* relative_path, ResourceBuffer* out_buffer);
|
||||
|
||||
/*
|
||||
* Release a loaded buffer and reset it to the empty state. Safe to call on an empty or
|
||||
* already-freed buffer.
|
||||
*/
|
||||
void (*free)(ModContext* ctx, ResourceBuffer* buffer);
|
||||
} ResourceService;
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "mods/service.hpp"
|
||||
|
||||
template <>
|
||||
struct dusk::mods::ServiceTraits<ResourceService> {
|
||||
static constexpr const char* id = RESOURCE_SERVICE_ID;
|
||||
static constexpr uint16_t major_version = RESOURCE_SERVICE_MAJOR;
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#include "mods/api.h"
|
||||
|
||||
#define TEXTURE_SERVICE_ID "dev.twilitrealm.dusklight.texture"
|
||||
#define TEXTURE_SERVICE_MAJOR 1u
|
||||
#define TEXTURE_SERVICE_MINOR 0u
|
||||
|
||||
/* Handle for a runtime texture replacement registration. 0 is never a valid handle. */
|
||||
typedef uint64_t TextureReplacementHandle;
|
||||
|
||||
typedef enum TextureKeyKind {
|
||||
/* Match a texture by the address of its in-memory GX texel data. */
|
||||
TEXTURE_KEY_POINTER = 0,
|
||||
/* Match by content: XXH64 of the base mip level (and of the referenced TLUT range for
|
||||
* palette formats), as encoded in replacement filenames / texture dumps. */
|
||||
TEXTURE_KEY_SOURCE = 1,
|
||||
} TextureKeyKind;
|
||||
|
||||
/* Wildcard values for TEXTURE_KEY_SOURCE hashes ("$" in the filename convention). */
|
||||
#define TEXTURE_HASH_WILDCARD UINT64_C(0xFFFFFFFFFFFFFFFF)
|
||||
#define TEXTURE_TLUT_WILDCARD UINT64_C(0xFFFFFFFFFFFFFFFE)
|
||||
|
||||
typedef struct TextureKey {
|
||||
uint32_t struct_size;
|
||||
TextureKeyKind kind;
|
||||
const void* pointer; /* TEXTURE_KEY_POINTER only */
|
||||
uint64_t texture_hash; /* TEXTURE_KEY_SOURCE */
|
||||
uint64_t tlut_hash; /* TEXTURE_KEY_SOURCE, palette formats only */
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t gx_format;
|
||||
bool has_tlut;
|
||||
} TextureKey;
|
||||
|
||||
#define TEXTURE_KEY_INIT {sizeof(TextureKey), TEXTURE_KEY_POINTER, NULL, 0u, 0u, 0u, 0u, 0u, false}
|
||||
|
||||
typedef struct TextureData {
|
||||
uint32_t struct_size;
|
||||
const void* data; /* texel data laid out in gx_format; copied by the service */
|
||||
size_t size;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t mip_count;
|
||||
uint32_t gx_format; /* any GX texture format supported by Aurora's converter */
|
||||
} TextureData;
|
||||
|
||||
#define TEXTURE_DATA_INIT {sizeof(TextureData), NULL, 0u, 0u, 0u, 1u, 0u}
|
||||
|
||||
/*
|
||||
* Runtime texture replacements.
|
||||
*
|
||||
* Registrations are owned by the calling mod and removed automatically when it is disabled,
|
||||
* reloaded, or fails. When multiple sources replace the same texture, the highest priority wins:
|
||||
* later-loaded mods beat earlier ones, and any mod beats the user's texture_replacements config
|
||||
* directory. Files shipped in a mod's textures/ directory register automatically with the same
|
||||
* ownership and priority; this service is for replacements decided at runtime.
|
||||
*/
|
||||
typedef struct TextureService {
|
||||
ServiceHeader header;
|
||||
|
||||
/* Register a replacement from raw texel data. The data is copied; the caller may free it as
|
||||
* soon as this returns. */
|
||||
ModResult (*register_data)(ModContext* ctx, const TextureKey* key, const TextureData* data,
|
||||
TextureReplacementHandle* out_handle);
|
||||
|
||||
/*
|
||||
* Register a replacement from an encoded .dds/.png inside the calling mod's bundle. The
|
||||
* filename encodes the key (same convention as the texture_replacements directory, e.g.
|
||||
* "tex1_{w}x{h}_{hash}_{fmt}.dds"); "_mipN" sidecars next to it are picked up automatically.
|
||||
* The file is decoded lazily on first use by the renderer.
|
||||
*/
|
||||
ModResult (*register_file)(ModContext* ctx, const char* bundle_path,
|
||||
TextureReplacementHandle* out_handle);
|
||||
|
||||
/* Remove a replacement previously registered by the calling mod. */
|
||||
ModResult (*unregister)(ModContext* ctx, TextureReplacementHandle handle);
|
||||
} TextureService;
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "mods/service.hpp"
|
||||
|
||||
template <>
|
||||
struct dusk::mods::ServiceTraits<TextureService> {
|
||||
static constexpr const char* id = TEXTURE_SERVICE_ID;
|
||||
static constexpr uint16_t major_version = TEXTURE_SERVICE_MAJOR;
|
||||
};
|
||||
#endif
|
||||
Reference in New Issue
Block a user