diff --git a/CMakeLists.txt b/CMakeLists.txt index 44213931f..cc11fac52 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,6 @@ file(GLOB GBA_SRC ${CMAKE_SOURCE_DIR}/src/gba/*.c) file(GLOB GBA_CHEATS_SRC ${CMAKE_SOURCE_DIR}/src/gba/cheats/*.c) file(GLOB GBA_RR_SRC ${CMAKE_SOURCE_DIR}/src/gba/rr/*.c) file(GLOB GBA_SV_SRC ${CMAKE_SOURCE_DIR}/src/gba/supervisor/*.c) -file(GLOB GUI_SRC ${CMAKE_SOURCE_DIR}/src/gui/*.c) file(GLOB UTIL_SRC ${CMAKE_SOURCE_DIR}/src/util/*.[cSs]) file(GLOB GUI_SRC ${CMAKE_SOURCE_DIR}/src/util/gui/*.c) file(GLOB RENDERER_SRC ${CMAKE_SOURCE_DIR}/src/gba/renderers/*.c) @@ -170,9 +169,19 @@ 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) - list(APPEND OS_LIB ctru) - file(GLOB OS_SRC ${CMAKE_SOURCE_DIR}/src/platform/3ds/3ds-*.c) + 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/*.c ${CMAKE_BINARY_DIR}/font.c) + if(USE_VFS_3DS) + add_definitions(-DUSE_VFS_3DS) + else() + add_definitions(-DUSE_VFS_FILE) + list(APPEND VFS_SRC ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-file.c ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-dirent.c) + endif() source_group("3DS-specific code" FILES ${OS_SRC}) elseif(WII) list(APPEND VFS_SRC ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-file.c ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-dirent.c) @@ -466,6 +475,12 @@ if(PSP2) add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/psp2 ${CMAKE_BINARY_DIR}/psp2) endif() +if(3DS) + 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() + if(BUILD_PERF) set(PERF_SRC ${CMAKE_SOURCE_DIR}/src/platform/test/perf-main.c) if(UNIX AND NOT APPLE) @@ -478,12 +493,6 @@ if(BUILD_PERF) install(FILES ${CMAKE_SOURCE_DIR}/tools/perf.py DESTINATION "${CMAKE_INSTALL_LIBDIR}/${BINARY_NAME}" COMPONENT ${BINARY_NAME}-perf) endif() -if(3DS) - add_executable(${BINARY_NAME}.elf ${CMAKE_SOURCE_DIR}/src/platform/3ds/main.c) - 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() - if(PSP) add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/psp ${CMAKE_BINARY_DIR}/psp) endif() diff --git a/src/platform/3ds/3ds-vfs.c b/src/platform/3ds/3ds-vfs.c index 38e7cc55a..0538b846a 100644 --- a/src/platform/3ds/3ds-vfs.c +++ b/src/platform/3ds/3ds-vfs.c @@ -5,7 +5,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "3ds-vfs.h" +#ifdef USE_VFS_3DS #include "util/memory.h" +#include "util/string.h" struct VFile3DS { struct VFile d; @@ -14,6 +16,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 +40,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 +164,85 @@ 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; + vd3d->vde.utf8Name = 0; + + 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; + memset(&vd3d->vde.ent, 0, sizeof(vd3d->vde.ent)); + if (vd3d->vde.utf8Name) { + free(vd3d->vde.utf8Name); + vd3d->vde.utf8Name = 0; + }; + FSDIR_Read(vd3d->handle, &n, 1, &vd3d->vde.ent); + if (!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) { + vd3de->utf8Name = utf16to8(vd3de->ent.name, sizeof(vd3de->ent.name) / 2); + } + return vd3de->utf8Name; +} +#endif diff --git a/src/platform/3ds/3ds-vfs.h b/src/platform/3ds/3ds-vfs.h index 52a146a78..5a386f154 100644 --- a/src/platform/3ds/3ds-vfs.h +++ b/src/platform/3ds/3ds-vfs.h @@ -12,6 +12,8 @@ #include <3ds.h> +extern FS_archive sdmcArchive; + struct VFile* VFileOpen3DS(FS_archive* archive, const char* path, int flags); #endif diff --git a/src/platform/3ds/CMakeToolchain.txt b/src/platform/3ds/CMakeToolchain.txt index 0cdb7f7e3..d7067d01e 100644 --- a/src/platform/3ds/CMakeToolchain.txt +++ b/src/platform/3ds/CMakeToolchain.txt @@ -34,6 +34,7 @@ set(CMAKE_MODULE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "module link flags") set(CMAKE_SHARED_LINKER_FLAGS ${link_flags} CACHE INTERNAL "shared link flags") set(3DSXTOOL ${toolchain_bin_dir}/3dsxtool) +set(RAW2C ${toolchain_bin_dir}/raw2c) set(3DS ON) add_definitions(-D_3DS -DARM11) diff --git a/src/platform/3ds/font.raw b/src/platform/3ds/font.raw new file mode 100644 index 000000000..05b1436df Binary files /dev/null and b/src/platform/3ds/font.raw differ diff --git a/src/platform/3ds/gui-font.c b/src/platform/3ds/gui-font.c new file mode 100644 index 000000000..bd241cb92 --- /dev/null +++ b/src/platform/3ds/gui-font.c @@ -0,0 +1,67 @@ +/* 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 - metric.padding.left, + y - GLYPH_HEIGHT, + (c & 15) * CELL_WIDTH, + (c >> 4) * CELL_HEIGHT, + CELL_WIDTH, + CELL_HEIGHT, + color); + x += metric.width; + } +} diff --git a/src/platform/3ds/main.c b/src/platform/3ds/main.c index 06597460c..ae74fa026 100644 --- a/src/platform/3ds/main.c +++ b/src/platform/3ds/main.c @@ -3,104 +3,150 @@ * 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 "gba/gba.h" -#include "gba/video.h" #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" #include <3ds.h> +#include + +FS_archive sdmcArchive; static void GBA3DSLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args); static Handle logFile; +static void _drawStart(void) { + sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); +} +static void _drawEnd(void) { + sf2d_end_frame(); + 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(); aptInit(); hidInit(0); - gfxInit(GSP_RGB565_OES, GSP_RGB565_OES, false); fsInit(); + sdmcInit(); - FS_archive sdmcArchive = (FS_archive) { + sf2d_init(); + sf2d_set_clear_color(0); + sf2d_texture* tex = sf2d_create_texture(256, 256, TEXFMT_RGB565, SF2D_PLACE_RAM); + memset(tex->data, 0, 256 * 256 * 2); + + sdmcArchive = (FS_archive) { ARCH_SDMC, - (FS_path) { PATH_EMPTY, 1, (u8*)"" }, + (FS_path) { PATH_EMPTY, 1, (const u8*)"" }, 0, 0 }; 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, + .logLevel = 0, + .idleOptimization = IDLE_LOOP_REMOVE + }; + GBAConfigLoadDefaults(&context.config, &opts); + context.gba->logHandler = GBA3DSLog; + context.gba->logLevel = 0; + struct GBAVideoSoftwareRenderer renderer; GBAVideoSoftwareRendererCreate(&renderer); + renderer.outputBuffer = anonymousMemoryMap(256 * VIDEO_VERTICAL_PIXELS * 2); + renderer.outputBufferStride = 256; + GBAVideoAssociateRenderer(&context.gba->video, &renderer.d); - size_t stride = VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL; - color_t* videoBuffer = anonymousMemoryMap(stride * VIDEO_VERTICAL_PIXELS); - struct GBA* gba = anonymousMemoryMap(sizeof(struct GBA)); - struct ARMCore* cpu = anonymousMemoryMap(sizeof(struct ARMCore)); - int activeKeys = 0; + if (!font) { + goto cleanup; + } - renderer.outputBuffer = videoBuffer; - renderer.outputBufferStride = VIDEO_HORIZONTAL_PIXELS; + struct GUIParams params = { + 320, 240, + font, _drawStart, _drawEnd, _pollInput + }; - struct VFile* rom = VFileOpen3DS(&sdmcArchive, "/rom.gba", FS_OPEN_READ); - struct VFile* save = VFileOpen3DS(&sdmcArchive, "/rom.sav", FS_OPEN_READ | FS_OPEN_WRITE); - - GBACreate(gba); - ARMSetComponents(cpu, &gba->d, 0, 0); - ARMInit(cpu); - - gba->keySource = &activeKeys; - gba->sync = 0; - - GBAVideoAssociateRenderer(&gba->video, &renderer.d); - - GBALoadROM(gba, rom, save, 0); - - gba->logHandler = GBA3DSLog; - - ARMReset(cpu); - - int frameCounter = 0; while (aptMainLoop()) { - ARMRunLoop(cpu); - - if (frameCounter != gba->video.frameCounter) { - u16 width, height; - u16* screen = (u16*) gfxGetFramebuffer(GFX_BOTTOM, GFX_BOTTOM, &height, &width); - u32 startX = (width - VIDEO_HORIZONTAL_PIXELS) / 2; - u32 startY = (height + VIDEO_VERTICAL_PIXELS) / 2 - 1; - u32 x, y; - for (y = 0; y < VIDEO_VERTICAL_PIXELS; ++y) { - for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) { - screen[startY - y + (startX + x) * height] = videoBuffer[y * VIDEO_HORIZONTAL_PIXELS + x]; - } - } - gfxFlushBuffers(); - gfxSwapBuffersGpu(); - gspWaitForVBlank(); + char path[256]; + if (!selectFile(¶ms, "/", path, sizeof(path), "gba")) { + break; + } + _drawStart(); + GUIFontPrintf(font, 130, (GUIFontHeight(font) + 240) / 2, GUI_TEXT_LEFT, 0xFFFFFFFF, "Loading..."); + _drawEnd(); + if (!GBAContextLoadROM(&context, path, true)) { + continue; + } + if (!GBAContextStart(&context)) { + continue; + } + while (aptMainLoop()) { hidScanInput(); - activeKeys = hidKeysHeld() & 0x3FF; + int activeKeys = hidKeysHeld() & 0x3FF; if (hidKeysDown() & KEY_X) { break; } - frameCounter = gba->video.frameCounter; + GBAContextFrame(&context, activeKeys); + GX_SetDisplayTransfer(0, renderer.outputBuffer, GX_BUFFER_DIM(256, VIDEO_VERTICAL_PIXELS), tex->data, GX_BUFFER_DIM(256, VIDEO_VERTICAL_PIXELS), 0x000002202); + GSPGPU_FlushDataCache(0, tex->data, 256 * VIDEO_VERTICAL_PIXELS * 2); + gspWaitForPPF(); + _drawStart(); + sf2d_draw_texture_scale(tex, 40, 296, 1, -1); + _drawEnd(); } + GBAContextStop(&context); } + GBAContextDeinit(&context); - ARMDeinit(cpu); - GBADestroy(gba); - - rom->close(rom); - save->close(save); - - mappedMemoryFree(gba, 0); - mappedMemoryFree(cpu, 0); - - mappedMemoryFree(videoBuffer, 0); +cleanup: + mappedMemoryFree(renderer.outputBuffer, 0); FSFILE_Close(logFile); + sf2d_free_texture(tex); + sf2d_fini(); + + sdmcExit(); fsExit(); gfxExit(); hidExit(); diff --git a/src/util/gui/file-select.c b/src/util/gui/file-select.c index 5a8a5d868..0f25d6db5 100644 --- a/src/util/gui/file-select.c +++ b/src/util/gui/file-select.c @@ -12,7 +12,7 @@ DECLARE_VECTOR(FileList, char*); DEFINE_VECTOR(FileList, char*); -void _cleanFiles(struct FileList* currentFiles) { +static void _cleanFiles(struct FileList* currentFiles) { size_t size = FileListSize(currentFiles); size_t i; for (i = 0; i < size; ++i) { @@ -21,7 +21,7 @@ void _cleanFiles(struct FileList* currentFiles) { FileListClear(currentFiles); } -void _upDirectory(char* currentPath) { +static void _upDirectory(char* currentPath) { char* end = strrchr(currentPath, '/'); if (!end) { return; @@ -37,7 +37,7 @@ void _upDirectory(char* currentPath) { // TODO: What if there was a trailing slash? } -bool _refreshDirectory(const char* currentPath, struct FileList* currentFiles) { +static bool _refreshDirectory(const char* currentPath, struct FileList* currentFiles) { _cleanFiles(currentFiles); struct VDir* dir = VDirOpen(currentPath); diff --git a/src/util/vfs.c b/src/util/vfs.c index 986327cfc..e13612263 100644 --- a/src/util/vfs.c +++ b/src/util/vfs.c @@ -9,6 +9,8 @@ #include "platform/psp/sce-vfs.h" #elif defined(PSP2) #include "platform/psp2/sce-vfs.h" +#elif _3DS +#include "platform/3ds/3ds-vfs.h" #endif struct VFile* VFileOpen(const char* path, int flags) { @@ -60,7 +62,35 @@ struct VFile* VFileOpen(const char* path, int flags) { sceFlags |= PSP2_O_CREAT; } return VFileOpenSce(path, sceFlags, 0666); ->>>>>>> port/psp2 +#elif defined(USE_VFS_3DS) + int ctrFlags = FS_OPEN_READ; + switch (flags & O_ACCMODE) { + case O_WRONLY: + ctrFlags = FS_OPEN_WRITE; + break; + case O_RDWR: + ctrFlags = FS_OPEN_READ | FS_OPEN_WRITE; + break; + case O_RDONLY: + ctrFlags = FS_OPEN_READ; + break; + } + + if (flags & O_CREAT) { + ctrFlags |= FS_OPEN_CREATE; + } + struct VFile* vf = VFileOpen3DS(&sdmcArchive, path, ctrFlags); + if (!vf) { + return 0; + } + if (flags & O_TRUNC) { + vf->truncate(vf, 0); + } + if (flags & O_APPEND) { + vf->seek(vf, vf->size(vf), SEEK_SET); + } + return vf; +>>>>>>> port/3ds #else return VFileOpenFD(path, flags); #endif