12#include <notcurses/notcurses.h>
23#define MAX_RESOURCES 128
25static int resource_count = 0;
39 log_msg(ERROR,
"media_output",
"Null Notcurses instance provided");
44 log_msg(ERROR,
"media_output",
"Null standard plane provided");
49 memset(resources, 0,
sizeof(resources));
66void media_cleanup(
void) {
68 for (
int i = 0; i < resource_count; i++) {
69 if (resources[i].is_loaded) {
70 free_media_resource(&resources[i]);
77 free_media_resource(media);
85 bool result = notcurses_render(nc);
88 log_msg(ERROR,
"media_output",
"Failed to refresh media display");
102 log_msg(ERROR,
"media_output",
"Invalid filename for load_media");
115 for (
int i = 0; i < resource_count; i++) {
116 if (strcmp(resources[i].path, filepath) == 0) {
118 return &resources[i];
123 if (resource_count >= MAX_RESOURCES) {
130 struct ncvisual* visual = ncvisual_from_file(filepath);
132 log_msg(ERROR,
"media_output",
"Failed to load media: %s (check if file exists)", filepath);
139 log_msg(ERROR,
"media_output",
"Failed to allocate memory for loaded visual");
140 ncvisual_destroy(visual);
147 resource->visual = visual;
148 resource->plane = NULL;
151 resource->media_type = media_type;
152 resource->is_loaded =
true;
153 resource->is_playing =
false;
154 resource->path = strdup(filepath);
155 if (!resource->path) {
156 log_msg(ERROR,
"media_output",
"Failed to store path");
157 ncvisual_destroy(visual);
164 struct ncvisual_options vopts = {0};
165 struct ncvgeom geom = {0};
166 int geo_ret = ncvisual_geom(nc, visual, &vopts, &geom);
169 log_msg(WARNING,
"media_output",
"Failed to get visual geometry, using default dimensions");
171 resource->og_width = 20;
172 resource->og_height = 20;
175 if (geom.pixx <= 0 || geom.pixy <= 0) {
176 log_msg(WARNING,
"media_output",
"Invalid media dimensions: %dx%d, using defaults",
177 geom.pixx, geom.pixy);
178 resource->og_width = 20;
179 resource->og_height = 20;
181 resource->og_width = geom.pixx;
182 resource->og_height = geom.pixy;
187 switch (media_type) {
189 resource->frames = 1;
193 resource->frames = 0;
194 while (ncvisual_decode(visual) != 1) {
198 ncvisual_destroy(visual);
199 resource->visual = ncvisual_from_file(filepath);
200 if (!resource->visual) {
201 log_msg(ERROR,
"media_output",
"Failed to reload GIF after frame counting");
202 free(resource->path);
210 log_msg(ERROR,
"media_output",
"MP4 loading not implemented yet");
211 ncvisual_destroy(visual);
213 free(resource->path);
216 log_msg(ERROR,
"media_output",
"Unsupported media media_type");
217 ncvisual_destroy(visual);
229 log_msg(ERROR,
"media_output",
"Null filename for display_media");
235 log_msg(ERROR,
"media_output",
"Invalid height (%d) for display_media", height);
241 if (!resource || !resource->is_loaded) {
242 log_msg(ERROR,
"media_output",
"Failed to load media: %s", filename);
250 resource->options.y = y;
251 resource->options.x = x;
254 if (resource->plane) {
255 ncplane_erase(resource->plane);
256 notcurses_render(nc);
257 ncplane_destroy(resource->plane);
258 resource->plane = NULL;
275 for (
int i = 0; i < resource_count; i++) {
276 if (strcmp(resources[i].path, filename) == 0) {
277 free_media_resource(&resources[i]);
280 if (i < resource_count - 1) {
281 memmove(&resources[i], &resources[i + 1], (resource_count - i - 1) *
sizeof(
loaded_visual_t));
334 int target_width,
int target_height) {
336 memset(&visual->options, 0,
sizeof(visual->options));
339 switch (scale_type) {
342 visual->options.scaling = NCSCALE_SCALE;
344 visual->options.leny = target_height > 0 ? target_height : 0;
345 visual->options.lenx = target_width > 0 ? target_width : 0;
350 if (target_width > 0 && target_height > 0) {
351 visual->options.scaling = NCSCALE_STRETCH;
352 visual->options.leny = target_height;
353 visual->options.lenx = target_width;
356 visual->options.scaling = NCSCALE_NONE;
362 visual->options.scaling = NCSCALE_SCALE;
363 visual->options.leny = 1;
364 visual->options.lenx = 1;
367 case SCALE_FULLSCREEN:
369 visual->options.scaling = NCSCALE_STRETCH;
371 int screen_width, screen_height;
373 visual->options.leny = screen_height;
374 visual->options.lenx = screen_width;
377 visual->options.leny = visual->og_height;
378 visual->options.lenx = visual->og_width;
379 log_msg(WARNING,
"media_output",
"Could not get screen dimensions, using original size");
386 visual->options.scaling = NCSCALE_NONE;
391 visual->options.blitter = NCBLIT_2x1;
411 log_msg(ERROR,
"media_output",
"Unsupported file extension for: %s", filename);
412 return MEDIA_UNSUPPORTED;
418 if (!filename || !extension) {
422 size_t filename_len = strlen(filename);
423 size_t ext_len = strlen(extension);
425 if (filename_len < ext_len) {
429 return strncasecmp(filename + filename_len - ext_len, extension, ext_len) == 0;
435 char* filepath = malloc(MAX_PATH_LEN);
437 log_msg(ERROR,
"media_output",
"Failed to allocate memory for file path");
442 const char* subdir =
"";
443 switch (media_type) {
458 snprintf(filepath, MAX_PATH_LEN,
"%s%s%s", MEDIA_PATH, subdir, filename);
474 if (!resource || !resource->is_loaded) {
479 resource->is_playing =
false;
482 if (resource->plane) {
484 ncplane_erase(resource->plane);
487 notcurses_render(nc);
490 ncplane_destroy(resource->plane);
491 resource->plane = NULL;
495 if (resource->visual) {
496 ncvisual_destroy(resource->visual);
497 resource->visual = NULL;
501 if (resource->path) {
502 free(resource->path);
503 resource->path = NULL;
507 resource->is_loaded =
false;
Exposes functions for the IO-Handler.
void log_msg(const log_level_t level, const char *module, const char *format,...)
Logs a formatted message with a specified log level and module.
Header file for logging functionality of the game.
bool get_screen_dimensions(int *width, int *height)
Get the dimensions of the standard plane.
Exposes functions for outputting to the console.