From 6778bfbdd2ac8006841de09351ff0ca8c58406dd Mon Sep 17 00:00:00 2001 From: KrutzOtrem Date: Sat, 7 Feb 2026 14:25:58 +0300 Subject: [PATCH 1/2] Add files via upload --- workspace/all/manual/manual.c | 470 ++++++++++++++++++++++++++ workspace/all/manual/manual.h | 9 + workspace/all/manual/manual_host.h | 35 ++ workspace/all/manual/manual_runtime.c | 89 +++++ 4 files changed, 603 insertions(+) create mode 100644 workspace/all/manual/manual.c create mode 100644 workspace/all/manual/manual.h create mode 100644 workspace/all/manual/manual_host.h create mode 100644 workspace/all/manual/manual_runtime.c diff --git a/workspace/all/manual/manual.c b/workspace/all/manual/manual.c new file mode 100644 index 000000000..ced1e9691 --- /dev/null +++ b/workspace/all/manual/manual.c @@ -0,0 +1,470 @@ +#include +#include +#include + +#include "manual.h" + +static ManualHost g_manual_host = {0}; + +void Manual_setHost(const ManualHost* host) { + if (host) g_manual_host = *host; + else memset(&g_manual_host, 0, sizeof(g_manual_host)); +} + +static SDL_Surface* Manual_getScreen(void) { + if (!g_manual_host.screen) return NULL; + return *g_manual_host.screen; +} + +static int Manual_shouldQuit(void) { + if (!g_manual_host.quit) return 0; + return *g_manual_host.quit; +} + +static void Manual_startFrame(void) { + if (g_manual_host.start_frame) g_manual_host.start_frame(); +} + +static void Manual_pollInput(void) { + if (g_manual_host.poll_input) g_manual_host.poll_input(); +} + +static void Manual_resetInput(void) { + if (g_manual_host.reset_input) g_manual_host.reset_input(); +} + +static int Manual_justPressed(ManualButton button) { + if (!g_manual_host.just_pressed) return 0; + return g_manual_host.just_pressed(button); +} + +static int Manual_isPressed(ManualButton button) { + if (!g_manual_host.is_pressed) return 0; + return g_manual_host.is_pressed(button); +} + +static void Manual_powerUpdate(int* dirty) { + if (g_manual_host.power_update) g_manual_host.power_update(dirty); +} + +static void Manual_clear(SDL_Surface* screen) { + if (!screen) return; + if (g_manual_host.clear) { + g_manual_host.clear(screen); + return; + } + SDL_FillRect(screen, NULL, 0); +} + +static void Manual_flip(SDL_Surface* screen) { + if (!screen) return; + if (g_manual_host.flip) g_manual_host.flip(screen); +} + +static void Manual_delay(void) { + if (g_manual_host.delay) g_manual_host.delay(); +} + +static void Manual_drawNotice(SDL_Surface* screen, const char* msg) { + if (g_manual_host.draw_notice) g_manual_host.draw_notice(screen, msg); +} + +static void Manual_drawOverlay(SDL_Surface* screen, const char* page_info) { + if (g_manual_host.draw_overlay) g_manual_host.draw_overlay(screen, page_info); +} + +static int Manual_fileExists(const char* path) { + FILE* fp = fopen(path, "rb"); + if (!fp) return 0; + fclose(fp); + return 1; +} + +static void Manual_showMessage(const char* msg) { + SDL_Surface* screen = Manual_getScreen(); + if (!screen || !msg) return; + + Manual_clear(screen); + Manual_drawNotice(screen, msg); + Manual_flip(screen); +} + +static void Manual_waitForDismiss(void) { + Manual_resetInput(); + while (!Manual_shouldQuit()) { + Manual_startFrame(); + Manual_pollInput(); + if (Manual_justPressed(MANUAL_BTN_A) || Manual_justPressed(MANUAL_BTN_B)) break; + Manual_powerUpdate(NULL); + Manual_delay(); + } +} + +#define MANUAL_MAX_PATH 4096 + +static int Manual_findLocalPath(const char* rom_path, char* out_path, + size_t out_sz) { + if (!rom_path || !rom_path[0] || !out_path || out_sz == 0) return 0; + + char rom_dir[MANUAL_MAX_PATH]; + strncpy(rom_dir, rom_path, sizeof(rom_dir)); + rom_dir[sizeof(rom_dir) - 1] = '\0'; + + char* slash = strrchr(rom_dir, '/'); + if (!slash) return 0; + + char rom_file[MANUAL_MAX_PATH]; + strncpy(rom_file, slash + 1, sizeof(rom_file)); + rom_file[sizeof(rom_file) - 1] = '\0'; + *slash = '\0'; + + char* dot = strrchr(rom_file, '.'); + if (dot) *dot = '\0'; + + snprintf(out_path, out_sz, "%s/.manuals/%s.pdf", rom_dir, rom_file); + if (Manual_fileExists(out_path)) return 1; + + snprintf(out_path, out_sz, "%s/.manuals/%s.PDF", rom_dir, rom_file); + if (Manual_fileExists(out_path)) return 1; + + out_path[0] = '\0'; + return 0; +} + +#ifdef ENABLE_PDF_MANUAL +#include + +#define MANUAL_ZOOM_STEP 1.05f +#define MANUAL_MIN_SCALE 0.10f +#define MANUAL_MAX_SCALE 6.00f +#define MANUAL_PAN_STEP 20.0f +#define MANUAL_VIEW_EPSILON 0.01f + +typedef struct { + fz_context* ctx; + fz_document* doc; + int page_count; + int current_page; + int view_align_right; + float page_w; + float page_h; + float scale; + float x_offset; + float y_offset; +} ManualState; + +static ManualState manual = {0}; + +static float Manual_clampf(float v, float lo, float hi) { + if (v < lo) return lo; + if (v > hi) return hi; + return v; +} + +static float Manual_fitHeightScale(SDL_Surface* screen) { + if (!screen || manual.page_h <= 0) return MANUAL_MIN_SCALE; + return Manual_clampf((float)screen->h / manual.page_h, + MANUAL_MIN_SCALE, MANUAL_MAX_SCALE); +} + +static int Manual_maxOffsetX(SDL_Surface* screen) { + if (!screen) return 0; + int scaled_w = (int)(manual.page_w * manual.scale); + int max_x = scaled_w - screen->w; + return max_x > 0 ? max_x : 0; +} + +static int Manual_maxOffsetY(SDL_Surface* screen) { + if (!screen) return 0; + int scaled_h = (int)(manual.page_h * manual.scale); + int max_y = scaled_h - screen->h; + return max_y > 0 ? max_y : 0; +} + +static int Manual_updatePageMetrics(void) { + if (!manual.ctx || !manual.doc) return 0; + + fz_page* page = NULL; + fz_try(manual.ctx) { + page = fz_load_page(manual.ctx, manual.doc, manual.current_page); + fz_rect bounds = fz_bound_page(manual.ctx, page); + manual.page_w = bounds.x1 - bounds.x0; + manual.page_h = bounds.y1 - bounds.y0; + } + fz_catch(manual.ctx) { + manual.page_w = 0; + manual.page_h = 0; + } + + if (page) fz_drop_page(manual.ctx, page); + return (manual.page_w > 0 && manual.page_h > 0); +} + +static void Manual_resetView(SDL_Surface* screen, int align_right) { + if (!screen || manual.page_h <= 0) return; + + manual.scale = Manual_fitHeightScale(screen); + manual.y_offset = 0; + manual.view_align_right = align_right ? 1 : 0; + + int max_x = Manual_maxOffsetX(screen); + manual.x_offset = align_right ? (float)max_x : 0.0f; +} + +static int Manual_isAtDefaultView(SDL_Surface* screen) { + if (!screen) return 1; + float expected_x = manual.view_align_right ? (float)Manual_maxOffsetX(screen) : 0.0f; + float x_diff = manual.x_offset - expected_x; + if (x_diff < 0) x_diff = -x_diff; + if (x_diff > MANUAL_VIEW_EPSILON) return 0; + if (manual.y_offset > MANUAL_VIEW_EPSILON) return 0; + float base_scale = Manual_fitHeightScale(screen); + float scale_diff = manual.scale - base_scale; + if (scale_diff < 0) scale_diff = -scale_diff; + return scale_diff <= MANUAL_VIEW_EPSILON; +} + +static int Manual_isZoomedIn(SDL_Surface* screen) { + if (!screen) return 0; + float base_scale = Manual_fitHeightScale(screen); + return manual.scale > (base_scale + MANUAL_VIEW_EPSILON); +} + +static void Manual_render(void) { + SDL_Surface* screen = Manual_getScreen(); + if (!screen || !manual.ctx || !manual.doc) return; + + fz_page* page = NULL; + fz_pixmap* pix = NULL; + + fz_try(manual.ctx) { page = fz_load_page(manual.ctx, manual.doc, manual.current_page); } + fz_catch(manual.ctx) { return; } + + fz_matrix ctm = fz_scale(manual.scale, manual.scale); + fz_try(manual.ctx) { + pix = fz_new_pixmap_from_page(manual.ctx, page, ctm, fz_device_rgb(manual.ctx), 0); + } + fz_catch(manual.ctx) { + fz_drop_page(manual.ctx, page); + return; + } + + int w = fz_pixmap_width(manual.ctx, pix); + int h = fz_pixmap_height(manual.ctx, pix); + int stride = fz_pixmap_stride(manual.ctx, pix); + unsigned char* samples = fz_pixmap_samples(manual.ctx, pix); + + Manual_clear(screen); + + SDL_Surface* page_surf = SDL_CreateRGBSurfaceWithFormatFrom( + samples, w, h, 24, stride, SDL_PIXELFORMAT_RGB24); + if (page_surf) { + SDL_Rect src = {0, 0, w, h}; + SDL_Rect dst = {0, 0, w, h}; + + if (w > screen->w) { + int max_x = w - screen->w; + manual.x_offset = Manual_clampf(manual.x_offset, 0.0f, (float)max_x); + src.x = (int)manual.x_offset; + src.w = screen->w; + dst.x = 0; + } else { + src.x = 0; + src.w = w; + dst.x = (screen->w - w) / 2; + manual.x_offset = 0; + } + + if (h > screen->h) { + int max_y = h - screen->h; + manual.y_offset = Manual_clampf(manual.y_offset, 0.0f, (float)max_y); + src.y = (int)manual.y_offset; + src.h = screen->h; + dst.y = 0; + } else { + src.y = 0; + src.h = h; + dst.y = (screen->h - h) / 2; + manual.y_offset = 0; + } + + dst.w = src.w; + dst.h = src.h; + SDL_BlitSurface(page_surf, &src, screen, &dst); + + SDL_FreeSurface(page_surf); + } + + char page_info[64]; + snprintf(page_info, sizeof(page_info), "%d / %d", manual.current_page + 1, + manual.page_count); + Manual_drawOverlay(screen, page_info); + Manual_flip(screen); + + fz_drop_pixmap(manual.ctx, pix); + fz_drop_page(manual.ctx, page); +} + +static void Manual_loop(const char* pdf_path) { + SDL_Surface* screen = Manual_getScreen(); + if (!screen || !pdf_path) return; + + manual.ctx = fz_new_context(NULL, NULL, FZ_STORE_DEFAULT); + if (!manual.ctx) return; + + fz_register_document_handlers(manual.ctx); + + fz_try(manual.ctx) { manual.doc = fz_open_document(manual.ctx, pdf_path); } + fz_catch(manual.ctx) { + fz_drop_context(manual.ctx); + manual.ctx = NULL; + return; + } + + fz_try(manual.ctx) { manual.page_count = fz_count_pages(manual.ctx, manual.doc); } + fz_catch(manual.ctx) { manual.page_count = 0; } + + if (manual.page_count <= 0 || !Manual_updatePageMetrics()) { + fz_drop_document(manual.ctx, manual.doc); + fz_drop_context(manual.ctx); + memset(&manual, 0, sizeof(manual)); + return; + } + + manual.current_page = 0; + Manual_resetView(screen, 0); + + int dirty = 1; + Manual_resetInput(); + + while (!Manual_shouldQuit()) { + Manual_startFrame(); + Manual_pollInput(); + + if (Manual_justPressed(MANUAL_BTN_B)) { + if (Manual_isAtDefaultView(screen)) break; + Manual_resetView(screen, manual.view_align_right); + dirty = 1; + continue; + } + + if (Manual_isZoomedIn(screen)) { + int max_x = Manual_maxOffsetX(screen); + if (Manual_isPressed(MANUAL_BTN_RIGHT)) { + manual.x_offset += MANUAL_PAN_STEP; + if (manual.x_offset > (float)max_x) manual.x_offset = (float)max_x; + dirty = 1; + } else if (Manual_isPressed(MANUAL_BTN_LEFT)) { + manual.x_offset -= MANUAL_PAN_STEP; + if (manual.x_offset < 0.0f) manual.x_offset = 0.0f; + dirty = 1; + } + } else if (Manual_justPressed(MANUAL_BTN_RIGHT)) { + int max_x = Manual_maxOffsetX(screen); + if (manual.x_offset < (float)max_x) { + manual.x_offset += screen->w * 0.90f; + if (manual.x_offset > (float)max_x) manual.x_offset = (float)max_x; + dirty = 1; + } else if (manual.current_page < manual.page_count - 1) { + manual.current_page++; + if (Manual_updatePageMetrics()) { + Manual_resetView(screen, 0); + dirty = 1; + } + } + } else if (Manual_justPressed(MANUAL_BTN_LEFT)) { + if (manual.x_offset > 0.0f) { + manual.x_offset -= screen->w * 0.90f; + if (manual.x_offset < 0.0f) manual.x_offset = 0.0f; + dirty = 1; + } else if (manual.current_page > 0) { + manual.current_page--; + if (Manual_updatePageMetrics()) { + Manual_resetView(screen, 1); + dirty = 1; + } + } + } + + if (Manual_isPressed(MANUAL_BTN_DOWN)) { + int max_y = Manual_maxOffsetY(screen); + if (max_y > 0) { + manual.y_offset += MANUAL_PAN_STEP; + if (manual.y_offset > (float)max_y) manual.y_offset = (float)max_y; + dirty = 1; + } + } else if (Manual_isPressed(MANUAL_BTN_UP)) { + if (manual.y_offset > 0.0f) { + manual.y_offset -= MANUAL_PAN_STEP; + if (manual.y_offset < 0.0f) manual.y_offset = 0.0f; + dirty = 1; + } + } else if (Manual_isPressed(MANUAL_BTN_R1)) { + float old_scale = manual.scale; + float cx = manual.x_offset + (screen->w * 0.5f); + float cy = manual.y_offset + (screen->h * 0.5f); + + manual.scale = Manual_clampf(manual.scale * MANUAL_ZOOM_STEP, + MANUAL_MIN_SCALE, MANUAL_MAX_SCALE); + if (manual.scale != old_scale) { + float s = manual.scale / old_scale; + manual.x_offset = (cx * s) - (screen->w * 0.5f); + manual.y_offset = (cy * s) - (screen->h * 0.5f); + dirty = 1; + } + } else if (Manual_isPressed(MANUAL_BTN_L1)) { + float old_scale = manual.scale; + float cx = manual.x_offset + (screen->w * 0.5f); + float cy = manual.y_offset + (screen->h * 0.5f); + + manual.scale = Manual_clampf(manual.scale / MANUAL_ZOOM_STEP, + MANUAL_MIN_SCALE, MANUAL_MAX_SCALE); + if (manual.scale != old_scale) { + float s = manual.scale / old_scale; + manual.x_offset = (cx * s) - (screen->w * 0.5f); + manual.y_offset = (cy * s) - (screen->h * 0.5f); + dirty = 1; + } + } + + manual.x_offset = Manual_clampf(manual.x_offset, 0.0f, + (float)Manual_maxOffsetX(screen)); + manual.y_offset = Manual_clampf(manual.y_offset, 0.0f, + (float)Manual_maxOffsetY(screen)); + + Manual_powerUpdate(&dirty); + + if (dirty) { + Manual_render(); + dirty = 0; + } else { + Manual_delay(); + } + } + + fz_drop_document(manual.ctx, manual.doc); + fz_drop_context(manual.ctx); + memset(&manual, 0, sizeof(manual)); +} +#endif + +void Manual_open(const char* rom_path) { + if (!rom_path || !rom_path[0]) return; + + SDL_Surface* screen = Manual_getScreen(); + if (!screen) return; + + char manual_path[MANUAL_MAX_PATH] = {0}; + if (!Manual_findLocalPath(rom_path, manual_path, sizeof(manual_path))) { + Manual_showMessage("No local manual found.\nPlace PDF in .manuals next to ROM."); + Manual_waitForDismiss(); + return; + } + +#ifdef ENABLE_PDF_MANUAL + Manual_loop(manual_path); +#else + Manual_showMessage("Manual support is disabled in this build."); + Manual_waitForDismiss(); +#endif +} diff --git a/workspace/all/manual/manual.h b/workspace/all/manual/manual.h new file mode 100644 index 000000000..9090db2c6 --- /dev/null +++ b/workspace/all/manual/manual.h @@ -0,0 +1,9 @@ +#ifndef __MANUAL_H__ +#define __MANUAL_H__ + +#include "manual_host.h" + +void Manual_setHost(const ManualHost* host); +void Manual_open(const char* rom_path); + +#endif diff --git a/workspace/all/manual/manual_host.h b/workspace/all/manual/manual_host.h new file mode 100644 index 000000000..5fafbee3b --- /dev/null +++ b/workspace/all/manual/manual_host.h @@ -0,0 +1,35 @@ +#ifndef __MANUAL_HOST_H__ +#define __MANUAL_HOST_H__ + +#include + +typedef enum ManualButton { + MANUAL_BTN_A = 0, + MANUAL_BTN_B, + MANUAL_BTN_UP, + MANUAL_BTN_DOWN, + MANUAL_BTN_LEFT, + MANUAL_BTN_RIGHT, + MANUAL_BTN_L1, + MANUAL_BTN_R1, +} ManualButton; + +typedef struct ManualHost { + SDL_Surface** screen; + int* quit; + void (*before_sleep)(void); + void (*after_sleep)(void); + void (*start_frame)(void); + void (*poll_input)(void); + void (*reset_input)(void); + int (*just_pressed)(ManualButton button); + int (*is_pressed)(ManualButton button); + void (*power_update)(int* dirty); + void (*clear)(SDL_Surface* screen); + void (*flip)(SDL_Surface* screen); + void (*delay)(void); + void (*draw_notice)(SDL_Surface* screen, const char* msg); + void (*draw_overlay)(SDL_Surface* screen, const char* page_info); +} ManualHost; + +#endif diff --git a/workspace/all/manual/manual_runtime.c b/workspace/all/manual/manual_runtime.c new file mode 100644 index 000000000..dd3bcd973 --- /dev/null +++ b/workspace/all/manual/manual_runtime.c @@ -0,0 +1,89 @@ +#include +#include + +#include "defines.h" +#include "api.h" +#include "manual.h" + +typedef void (*manual_set_host_fn)(const ManualHost *host); +typedef void (*manual_open_fn)(const char *rom_path); + +static ManualHost g_manual_host; +static int g_host_initialized = 0; +static int g_plugin_load_attempted = 0; +static void *g_plugin_handle = NULL; +static manual_set_host_fn g_plugin_set_host = NULL; +static manual_open_fn g_plugin_open = NULL; + +static void Manual_tryLoadPlugin(void) { + if (g_plugin_load_attempted) + return; + g_plugin_load_attempted = 1; + + const char *paths[] = { + "/mnt/SDCARD/.system/tg5040/lib/manual.so", + SYSTEM_PATH "/lib/manual.so", + "/mnt/SDCARD/.system/tg5040/lib/libmanual.so", + SYSTEM_PATH "/lib/libmanual.so", + "./manual.so", + "./libmanual.so", + "manual.so", + "libmanual.so", + NULL}; + for (int i = 0; paths[i]; i++) { + g_plugin_handle = dlopen(paths[i], RTLD_NOW | RTLD_LOCAL); + if (g_plugin_handle) + break; + } + + if (!g_plugin_handle) { + const char *err = dlerror(); + LOG_error("manual: failed to load manual.so (%s)\n", + err ? err : "unknown error"); + return; + } + + g_plugin_set_host = (manual_set_host_fn)dlsym(g_plugin_handle, "Manual_setHost"); + g_plugin_open = (manual_open_fn)dlsym(g_plugin_handle, "Manual_open"); + + if (!g_plugin_set_host || !g_plugin_open) { + LOG_error("manual: missing required symbols in manual.so\n"); + dlclose(g_plugin_handle); + g_plugin_handle = NULL; + g_plugin_set_host = NULL; + g_plugin_open = NULL; + return; + } + + if (g_host_initialized) + g_plugin_set_host(&g_manual_host); +} + +void Manual_setHost(const ManualHost *host) { + if (host) { + g_manual_host = *host; + g_host_initialized = 1; + } else { + memset(&g_manual_host, 0, sizeof(g_manual_host)); + g_host_initialized = 0; + } + + Manual_tryLoadPlugin(); + if (g_plugin_set_host && g_host_initialized) + g_plugin_set_host(&g_manual_host); +} + +void Manual_open(const char *rom_path) { + if (!rom_path || !rom_path[0]) + return; + + Manual_tryLoadPlugin(); + if (!g_plugin_open) { + LOG_error("manual: plugin unavailable, cannot open manual for %s\n", rom_path); + return; + } + + if (g_plugin_set_host && g_host_initialized) + g_plugin_set_host(&g_manual_host); + g_plugin_open(rom_path); +} From 2ec336d630c779bc3923529751fead8994c693d2 Mon Sep 17 00:00:00 2001 From: KrutzOtrem Date: Sat, 7 Feb 2026 14:26:33 +0300 Subject: [PATCH 2/2] Add files via upload --- workspace/all/minarch/makefile | 28 +++++++-- workspace/all/minarch/minarch.c | 104 +++++++++++++++++++++++++++++++- 2 files changed, 126 insertions(+), 6 deletions(-) diff --git a/workspace/all/minarch/makefile b/workspace/all/minarch/makefile index 56b3f7138..cc4f35284 100644 --- a/workspace/all/minarch/makefile +++ b/workspace/all/minarch/makefile @@ -21,14 +21,19 @@ SDL?=SDL TARGET = minarch PRODUCT= build/$(PLATFORM)/$(TARGET).elf -INCDIR = -I. -I./libretro-common/include/ -I../common/ -I../../$(PLATFORM)/platform/ -SOURCE = $(TARGET).c ../common/scaler.c ../common/utils.c ../common/config.c ../common/api.c ../../$(PLATFORM)/platform/platform.c +MANUAL_PRODUCT = build/$(PLATFORM)/manual.so +INCDIR = -I. -I./libretro-common/include/ -I../common/ -I../manual/ -I../../$(PLATFORM)/platform/ +SOURCE = $(TARGET).c ../manual/manual_runtime.c ../common/scaler.c ../common/utils.c ../common/config.c ../common/api.c ../../$(PLATFORM)/platform/platform.c +MANUAL_SOURCE = ../manual/manual.c +MANUAL_CFLAGS = +MANUAL_LIBS = CC = $(CROSS_COMPILE)gcc CFLAGS += $(OPT) CFLAGS += $(INCDIR) -DPLATFORM=\"$(PLATFORM)\" -std=gnu99 LDFLAGS += -lmsettings -lsamplerate LDFLAGS += -llz4 +LDFLAGS += -ldl ifeq ($(PROFILE), 1) CFLAGS += -pg @@ -53,6 +58,14 @@ CFLAGS += -DHAS_SRM LDFLAGS += -lasound endif +ifeq ($(PLATFORM), tg5040) +TG5040_LIBS ?= ../../tg5040/libs +ifneq (,$(wildcard $(TG5040_LIBS)/lib/libmupdf.a)) +MANUAL_CFLAGS += -I$(TG5040_LIBS)/include -DENABLE_PDF_MANUAL +MANUAL_LIBS += -L$(TG5040_LIBS)/lib -lmupdf -lmupdf-third -lm +endif +endif + # CFLAGS += -Wall -Wno-unused-variable -Wno-unused-function -Wno-format-overflow ifeq ($(PLATFORM), desktop) @@ -74,11 +87,11 @@ BUILD_HASH!=cat ../../hash.txt CFLAGS += -DBUILD_DATE=\"${BUILD_DATE}\" -DBUILD_HASH=\"${BUILD_HASH}\" ifeq ($(PLATFORM), desktop) -all: clean libretro-common $(PREFIX_LOCAL)/include/msettings.h +all: clean libretro-common $(PREFIX_LOCAL)/include/msettings.h manual-plugin mkdir -p build/$(PLATFORM) $(CC) $(SOURCE) -o $(PRODUCT) $(CFLAGS) $(LDFLAGS) else -all: clean libretro-common libsrm.a $(PREFIX_LOCAL)/include/msettings.h +all: clean libretro-common libsrm.a $(PREFIX_LOCAL)/include/msettings.h manual-plugin mkdir -p build/$(PLATFORM) cp -L $(PREFIX)/lib/libsamplerate.so.* build/$(PLATFORM) # This is a bandaid fix, needs to be cleaned up if/when we expand to other platforms. @@ -90,6 +103,10 @@ all: clean libretro-common libsrm.a $(PREFIX_LOCAL)/include/msettings.h $(CC) $(SOURCE) -o $(PRODUCT) $(CFLAGS) $(LDFLAGS) endif +manual-plugin: + mkdir -p build/$(PLATFORM) + $(CC) $(MANUAL_SOURCE) -o $(MANUAL_PRODUCT) $(CFLAGS) $(MANUAL_CFLAGS) -fPIC -shared $(MANUAL_LIBS) + libretro-common: git clone https://github.com/libretro/libretro-common @@ -107,4 +124,5 @@ libsrm.a: $(OBJECTS) clean: rm -f $(PRODUCT) - rm -f $(OBJECTS) $(LIBRARY) \ No newline at end of file + rm -f $(MANUAL_PRODUCT) + rm -f $(OBJECTS) $(LIBRARY) diff --git a/workspace/all/minarch/minarch.c b/workspace/all/minarch/minarch.c index cb7e6e06d..d6be72b57 100644 --- a/workspace/all/minarch/minarch.c +++ b/workspace/all/minarch/minarch.c @@ -29,6 +29,7 @@ #include #include #include +#include "manual.h" /////////////////////////////////////// @@ -6110,13 +6111,14 @@ void Core_close(void) { /////////////////////////////////////// -#define MENU_ITEM_COUNT 5 +#define MENU_ITEM_COUNT 6 #define MENU_SLOT_COUNT 8 enum { ITEM_CONT, ITEM_SAVE, ITEM_LOAD, + ITEM_MANUAL, ITEM_OPTS, ITEM_QUIT, }; @@ -6158,6 +6160,7 @@ static struct { [ITEM_CONT] = "Continue", [ITEM_SAVE] = "Save", [ITEM_LOAD] = "Load", + [ITEM_MANUAL] = "Manual", [ITEM_OPTS] = "Options", [ITEM_QUIT] = "Quit", } @@ -6231,6 +6234,82 @@ void Menu_afterSleep() { setOverclock(overclock); } +static int ManualHost_toBtn(ManualButton button) { + switch (button) { + case MANUAL_BTN_A: return BTN_A; + case MANUAL_BTN_B: return BTN_B; + case MANUAL_BTN_UP: return BTN_UP; + case MANUAL_BTN_DOWN: return BTN_DOWN; + case MANUAL_BTN_LEFT: return BTN_LEFT; + case MANUAL_BTN_RIGHT: return BTN_RIGHT; + case MANUAL_BTN_L1: return BTN_L1; + case MANUAL_BTN_R1: return BTN_R1; + default: return 0; + } +} + +static void ManualHost_startFrame(void) { + GFX_startFrame(); +} + +static void ManualHost_pollInput(void) { + PAD_poll(); +} + +static void ManualHost_resetInput(void) { + PAD_reset(); +} + +static int ManualHost_justPressed(ManualButton button) { + int btn = ManualHost_toBtn(button); + if (!btn) return 0; + return PAD_justPressed(btn); +} + +static int ManualHost_isPressed(ManualButton button) { + int btn = ManualHost_toBtn(button); + if (!btn) return 0; + return PAD_isPressed(btn); +} + +static void ManualHost_powerUpdate(int* dirty) { + PWR_update(dirty, NULL, Menu_beforeSleep, Menu_afterSleep); +} + +static void ManualHost_clear(SDL_Surface* dst) { + if (!dst) return; + GFX_clear(dst); +} + +static void ManualHost_flip(SDL_Surface* dst) { + if (!dst) return; + GFX_flip(dst); +} + +static void ManualHost_delay(void) { + GFX_delay(); +} + +static void ManualHost_drawNotice(SDL_Surface* dst, const char* msg) { + if (!dst || !msg) return; + GFX_blitMessage(font.medium, (char*)msg, dst, + &(SDL_Rect){SCALE1(PADDING), SCALE1(PADDING), + dst->w - SCALE1(PADDING * 2), + dst->h - SCALE1(PADDING * 2)}); + GFX_blitButtonGroup((char*[]){"B", "BACK", NULL}, 1, dst, 1); +} + +static void ManualHost_drawOverlay(SDL_Surface* dst, const char* page_info) { + if (!dst) return; + GFX_blitButtonGroup((char*[]){"B", "BACK", NULL}, 1, dst, 0); + GFX_blitButtonGroup((char*[]){"L1/R1", "ZOOM", NULL}, 1, dst, 1); + if (page_info && page_info[0]) { + GFX_blitMessage(font.small, (char*)page_info, dst, + &(SDL_Rect){SCALE1(PADDING), SCALE1(PADDING), + SCALE1(90), SCALE1(PILL_SIZE)}); + } +} + typedef struct MenuList MenuList; typedef struct MenuItem MenuItem; enum { @@ -7914,6 +7993,12 @@ static void Menu_loop(void) { show_menu = 0; } break; + case ITEM_MANUAL: { + Manual_open(game.path); + PAD_reset(); + dirty = 1; + } + break; case ITEM_OPTS: { if (simple_mode) { core.reset(); @@ -8339,6 +8424,23 @@ int main(int argc , char* argv[]) { SND_registerDeviceWatcher(onAudioSinkChanged); InitSettings(); // after we initialize audio Menu_init(); + Manual_setHost(&(ManualHost){ + .screen = &screen, + .quit = &quit, + .before_sleep = Menu_beforeSleep, + .after_sleep = Menu_afterSleep, + .start_frame = ManualHost_startFrame, + .poll_input = ManualHost_pollInput, + .reset_input = ManualHost_resetInput, + .just_pressed = ManualHost_justPressed, + .is_pressed = ManualHost_isPressed, + .power_update = ManualHost_powerUpdate, + .clear = ManualHost_clear, + .flip = ManualHost_flip, + .delay = ManualHost_delay, + .draw_notice = ManualHost_drawNotice, + .draw_overlay = ManualHost_drawOverlay, + }); State_resume(); Menu_initState(); // make ready for state shortcuts