From 1b78b2133827584224f210d3ae7027e9289cfc04 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 23 Aug 2015 17:18:28 -0700 Subject: [PATCH] 3DS: Add GUIFont --- CMakeLists.txt | 7 ++- src/platform/3ds/3ds-vfs.c | 98 ++++++++++++++++++++++++++++++++++++ src/platform/3ds/font.raw | Bin 0 -> 65536 bytes src/platform/3ds/gui-font.c | 65 ++++++++++++++++++++++++ src/platform/3ds/main.c | 50 +++++++++++++++++- 5 files changed, 217 insertions(+), 3 deletions(-) create mode 100644 src/platform/3ds/font.raw create mode 100644 src/platform/3ds/gui-font.c diff --git a/CMakeLists.txt b/CMakeLists.txt index ca89e4ce0..482a8ce09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -169,10 +169,13 @@ elseif(UNIX) file(GLOB OS_SRC ${CMAKE_SOURCE_DIR}/src/platform/posix/*.c) source_group("POSIX-specific code" FILES ${OS_SRC}) elseif(3DS) + set(M_LIBRARY m) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-format") add_definitions(-DCOLOR_16_BIT -DCOLOR_5_6_5) + execute_process(COMMAND ${RAW2C} ${CMAKE_SOURCE_DIR}/src/platform/3ds/font.raw) + include_directories(${CMAKE_BINARY_DIR}) list(APPEND OS_LIB sf2d ctru) - file(GLOB OS_SRC ${CMAKE_SOURCE_DIR}/src/platform/3ds/3ds-*.c) + file(GLOB OS_SRC ${CMAKE_SOURCE_DIR}/src/platform/3ds/*.c ${CMAKE_BINARY_DIR}/font.c) source_group("3DS-specific code" FILES ${OS_SRC}) endif() @@ -443,7 +446,7 @@ if(BUILD_QT) endif() if(3DS) - add_executable(${BINARY_NAME}.elf ${CMAKE_SOURCE_DIR}/src/platform/3ds/main.c) + add_executable(${BINARY_NAME}.elf ${GUI_SRC}) target_link_libraries(${BINARY_NAME}.elf ${BINARY_NAME} m ${OS_LIB}) add_custom_command(TARGET ${BINARY_NAME}.elf POST_BUILD COMMAND ${3DSXTOOL} ${BINARY_NAME}.elf ${BINARY_NAME}.3dsx) endif() diff --git a/src/platform/3ds/3ds-vfs.c b/src/platform/3ds/3ds-vfs.c index 38e7cc55a..d2fc0e545 100644 --- a/src/platform/3ds/3ds-vfs.c +++ b/src/platform/3ds/3ds-vfs.c @@ -6,6 +6,7 @@ #include "3ds-vfs.h" #include "util/memory.h" +#include "util/string.h" struct VFile3DS { struct VFile d; @@ -14,6 +15,20 @@ struct VFile3DS { u64 offset; }; +struct VDirEntry3DS { + struct VDirEntry d; + FS_dirent ent; + char* utf8Name; +}; + +struct VDir3DS { + struct VDir d; + + char* path; + Handle handle; + struct VDirEntry3DS vde; +}; + static bool _vf3dClose(struct VFile* vf); static off_t _vf3dSeek(struct VFile* vf, off_t offset, int whence); static ssize_t _vf3dRead(struct VFile* vf, void* buffer, size_t size); @@ -24,6 +39,13 @@ static void _vf3dTruncate(struct VFile* vf, size_t size); static ssize_t _vf3dSize(struct VFile* vf); static bool _vf3dSync(struct VFile* vf, const void* buffer, size_t size); +static bool _vd3dClose(struct VDir* vd); +static void _vd3dRewind(struct VDir* vd); +static struct VDirEntry* _vd3dListNext(struct VDir* vd); +static struct VFile* _vd3dOpenFile(struct VDir* vd, const char* path, int mode); + +static const char* _vd3deName(struct VDirEntry* vde); + struct VFile* VFileOpen3DS(FS_archive* archive, const char* path, int flags) { struct VFile3DS* vf3d = malloc(sizeof(struct VFile3DS)); if (!vf3d) { @@ -141,3 +163,79 @@ static bool _vf3dSync(struct VFile* vf, const void* buffer, size_t size) { FSFILE_Flush(vf3d->handle); return true; } + +struct VDir* VDirOpen(const char* path) { + struct VDir3DS* vd3d = malloc(sizeof(struct VDir3DS)); + if (!vd3d) { + return 0; + } + + FS_path newPath = FS_makePath(PATH_CHAR, path); + Result res = FSUSER_OpenDirectory(0, &vd3d->handle, sdmcArchive, newPath); + if (res & 0xFFFC03FF) { + free(vd3d); + return 0; + } + + vd3d->path = strdup(path); + + vd3d->d.close = _vd3dClose; + vd3d->d.rewind = _vd3dRewind; + vd3d->d.listNext = _vd3dListNext; //// Crashes here for no good reason + vd3d->d.openFile = _vd3dOpenFile; + + vd3d->vde.d.name = _vd3deName; + + return &vd3d->d; +} + +static bool _vd3dClose(struct VDir* vd) { + struct VDir3DS* vd3d = (struct VDir3DS*) vd; + FSDIR_Close(vd3d->handle); + free(vd3d->path); + if (vd3d->vde.utf8Name) { + free(vd3d->vde.utf8Name); + } + free(vd3d); + return true; +} + +static void _vd3dRewind(struct VDir* vd) { + struct VDir3DS* vd3d = (struct VDir3DS*) vd; + FSDIR_Close(vd3d->handle); + FS_path newPath = FS_makePath(PATH_CHAR, vd3d->path); + FSUSER_OpenDirectory(0, &vd3d->handle, sdmcArchive, newPath); +} + +static struct VDirEntry* _vd3dListNext(struct VDir* vd) { + struct VDir3DS* vd3d = (struct VDir3DS*) vd; + u32 n = 0; + Result res = FSDIR_Read(vd3d->handle, &n, 1, &vd3d->vde.ent); + if (res & 0xFFFC03FF || !n) { + return 0; + } + return &vd3d->vde.d; +} + +static struct VFile* _vd3dOpenFile(struct VDir* vd, const char* path, int mode) { + struct VDir3DS* vd3d = (struct VDir3DS*) vd; + if (!path) { + return 0; + } + const char* dir = vd3d->path; + char* combined = malloc(sizeof(char) * (strlen(path) + strlen(dir) + 2)); + sprintf(combined, "%s/%s", dir, path); + + struct VFile* file = VFileOpen(combined, mode); + free(combined); + return file; +} + +static const char* _vd3deName(struct VDirEntry* vde) { + struct VDirEntry3DS* vd3de = (struct VDirEntry3DS*) vde; + if (vd3de->utf8Name) { + free(vd3de->utf8Name); + } + vd3de->utf8Name = utf16to8(vd3de->ent.name, sizeof(vd3de->ent.name) / 2); + return vd3de->utf8Name; +} diff --git a/src/platform/3ds/font.raw b/src/platform/3ds/font.raw new file mode 100644 index 0000000000000000000000000000000000000000..aa7abf5a4126107bd10db4505f722a44e691976b GIT binary patch literal 65536 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 LECrSVpH<+0tX?pb literal 0 HcmV?d00001 diff --git a/src/platform/3ds/gui-font.c b/src/platform/3ds/gui-font.c new file mode 100644 index 000000000..5df92ed9e --- /dev/null +++ b/src/platform/3ds/gui-font.c @@ -0,0 +1,65 @@ +/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "util/gui/font.h" +#include "util/gui/font-metrics.h" +#include "util/png-io.h" +#include "util/vfs.h" +#include "font.h" + +#include + +#define CELL_HEIGHT 16 +#define CELL_WIDTH 16 +#define GLYPH_HEIGHT 12 + +struct GUIFont { + sf2d_texture* tex; +}; + +struct GUIFont* GUIFontCreate(void) { + struct GUIFont* guiFont = malloc(sizeof(struct GUIFont)); + if (!guiFont) { + return 0; + } + guiFont->tex = sf2d_create_texture(256, 128, TEXFMT_RGB5A1, SF2D_PLACE_RAM); + memcpy(guiFont->tex->data, font, font_size); + guiFont->tex->tiled = 1; + return guiFont; +} + +void GUIFontDestroy(struct GUIFont* font) { + sf2d_free_texture(font->tex); + free(font); +} + +int GUIFontHeight(const struct GUIFont* font) { + UNUSED(font); + return GLYPH_HEIGHT; +} + +void GUIFontPrintf(const struct GUIFont* font, int x, int y, enum GUITextAlignment align, uint32_t color, const char* text, ...) { + UNUSED(align); // TODO + char buffer[256]; + va_list args; + va_start(args, text); + int len = vsnprintf(buffer, sizeof(buffer), text, args); + va_end(args); + int i; + for (i = 0; i < len; ++i) { + char c = buffer[i]; + if (c > 0x7F) { + c = 0; + } + struct GUIFontGlyphMetric metric = defaultFontMetrics[c]; + sf2d_draw_texture_part_blend(font->tex, x, y - GLYPH_HEIGHT + metric.padding.top, + (c & 15) * CELL_WIDTH + metric.padding.left, + (c >> 4) * CELL_HEIGHT + metric.padding.top, + CELL_WIDTH - (metric.padding.left + metric.padding.right), + CELL_HEIGHT - (metric.padding.top + metric.padding.bottom), + color); + x += metric.width; + } +} diff --git a/src/platform/3ds/main.c b/src/platform/3ds/main.c index d2d7c88f2..1a212d982 100644 --- a/src/platform/3ds/main.c +++ b/src/platform/3ds/main.c @@ -7,6 +7,9 @@ #include "gba/renderers/video-software.h" #include "gba/supervisor/context.h" #include "gba/video.h" +#include "util/gui.h" +#include "util/gui/file-select.h" +#include "util/gui/font.h" #include "util/memory.h" #include "3ds-vfs.h" @@ -27,6 +30,34 @@ static void _drawEnd(void) { sf2d_swapbuffers(); } +static int _pollInput(void) { + hidScanInput(); + int keys = 0; + int activeKeys = hidKeysHeld(); + if (activeKeys & KEY_X) { + keys |= 1 << GUI_INPUT_CANCEL; + } + if (activeKeys & KEY_B) { + keys |= 1 << GUI_INPUT_BACK; + } + if (activeKeys & KEY_A) { + keys |= 1 << GUI_INPUT_SELECT; + } + if (activeKeys & KEY_LEFT) { + keys |= 1 << GUI_INPUT_LEFT; + } + if (activeKeys & KEY_RIGHT) { + keys |= 1 << GUI_INPUT_RIGHT; + } + if (activeKeys & KEY_UP) { + keys |= 1 << GUI_INPUT_UP; + } + if (activeKeys & KEY_DOWN) { + keys |= 1 << GUI_INPUT_DOWN; + } + return keys; +} + int main() { struct GBAContext context; srvInit(); @@ -46,6 +77,8 @@ int main() { FSUSER_OpenArchive(0, &sdmcArchive); FSUSER_OpenFile(0, &logFile, sdmcArchive, FS_makePath(PATH_CHAR, "/mgba.log"), FS_OPEN_WRITE | FS_OPEN_CREATE, FS_ATTRIBUTE_NONE); + struct GUIFont* font = GUIFontCreate(); + GBAContextInit(&context, 0); struct GBAOptions opts = { .useBios = true, @@ -54,6 +87,7 @@ int main() { }; GBAConfigLoadDefaults(&context.config, &opts); context.gba->logHandler = GBA3DSLog; + context.gba->logLevel = 0; struct GBAVideoSoftwareRenderer renderer; GBAVideoSoftwareRendererCreate(&renderer); @@ -61,7 +95,21 @@ int main() { renderer.outputBufferStride = 256; GBAVideoAssociateRenderer(&context.gba->video, &renderer.d); - GBAContextLoadROM(&context, "/rom.gba", true); + if (!font) { + goto cleanup; + } + + struct GUIParams params = { + 320, 240, + font, _drawStart, _drawEnd, _pollInput + }; + _drawStart(); + GUIFontPrintf(font, 20, 20, GUI_TEXT_LEFT, 0xFFFFFFFF, "Loading..."); + _drawEnd(); + char path[256] = "/rom.gba"; + if (!selectFile(¶ms, "/", path, sizeof(path), "gba") || !GBAContextLoadROM(&context, path, true)) { + goto cleanup; + } GBAContextStart(&context); while (aptMainLoop()) {