diff --git a/CMakeLists.txt b/CMakeLists.txt index 71935705f..443bc4b8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -169,9 +169,10 @@ elseif(3DS) file(GLOB OS_SRC ${CMAKE_SOURCE_DIR}/src/platform/3ds/3ds-*.c) source_group("3DS-specific code" FILES ${OS_SRC}) elseif(WII) - list(APPEND VFS_SRC ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-file.c) + list(APPEND VFS_SRC ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-file.c ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-dirent.c) add_definitions(-DCOLOR_16_BIT -DCOLOR_5_6_5 -DUSE_VFS_FILE) - file(GLOB OS_SRC ${CMAKE_SOURCE_DIR}/src/platform/wii/wii-*.c) + include_directories(${CMAKE_BINARY_DIR}) + file(GLOB OS_SRC ${CMAKE_SOURCE_DIR}/src/platform/wii/*.c) list(APPEND OS_LIB fat ogc) source_group("Wii-specific code" FILES ${OS_SRC}) endif() @@ -198,7 +199,8 @@ endif() if(WII) add_definitions(-U__STRICT_ANSI__) - add_executable(${BINARY_NAME}.elf ${CMAKE_SOURCE_DIR}/src/platform/wii/main.c) + execute_process(COMMAND ${RAW2C} ${CMAKE_SOURCE_DIR}/src/platform/wii/font.tpl OUTPUT_QUIET ERROR_QUIET) + add_executable(${BINARY_NAME}.elf ${GUI_SRC} ${CMAKE_BINARY_DIR}/font.c) target_link_libraries(${BINARY_NAME}.elf ${BINARY_NAME} ${OS_LIB}) add_custom_command(TARGET ${BINARY_NAME}.elf POST_BUILD COMMAND ${ELF2DOL} ${BINARY_NAME}.elf ${BINARY_NAME}.dol) endif() diff --git a/src/platform/wii/CMakeToolchain.txt b/src/platform/wii/CMakeToolchain.txt index c33265feb..9e78b43ce 100644 --- a/src/platform/wii/CMakeToolchain.txt +++ b/src/platform/wii/CMakeToolchain.txt @@ -34,6 +34,8 @@ 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(ELF2DOL ${toolchain_bin_dir}/elf2dol) +set(GXTEXCONV ${toolchain_bin_dir}/gxtexconv) +set(RAW2C ${toolchain_bin_dir}/raw2c) set(WII ON) add_definitions(-DGEKKO) diff --git a/src/platform/wii/font.tpl b/src/platform/wii/font.tpl new file mode 100644 index 000000000..e0b1a1c55 Binary files /dev/null and b/src/platform/wii/font.tpl differ diff --git a/src/platform/wii/gui-font.c b/src/platform/wii/gui-font.c new file mode 100644 index 000000000..7c0b0997a --- /dev/null +++ b/src/platform/wii/gui-font.c @@ -0,0 +1,87 @@ +/* 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 "font.h" + +#include + +#define GLYPH_HEIGHT 11 +#define GLYPH_WIDTH 14 +#define FONT_TRACKING 10 +#define CELL_HEIGHT 16 +#define CELL_WIDTH 16 + +struct GUIFont { + TPLFile tdf; +}; + +struct GUIFont* GUIFontCreate(void) { + struct GUIFont* guiFont = malloc(sizeof(struct GUIFont)); + if (!guiFont) { + return 0; + } + + // libogc's TPL code modifies and frees this itself... + void* fontTpl = memalign(32, font_size); + if (!fontTpl) { + free(guiFont); + return 0; + } + memcpy(fontTpl, font, font_size); + TPL_OpenTPLFromMemory(&guiFont->tdf, fontTpl, font_size); + return guiFont; +} + +void GUIFontDestroy(struct GUIFont* font) { + TPL_CloseTPLFile(&font->tdf); + 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; + GXTexObj tex; + // Grumble grumble, libogc is bad about const-correctness + struct GUIFont* ncfont = font; + TPL_GetTexture(&ncfont->tdf, 0, &tex); + GX_LoadTexObj(&tex, GX_TEXMAP0); + + GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); + + for (i = 0; i < len; ++i) { + char c = buffer[i]; + if (c > 0x7F) { + c = 0; + } + s16 tx = (c & 15) * CELL_WIDTH + ((CELL_WIDTH - GLYPH_WIDTH) >> 1); + s16 ty = (c >> 4) * CELL_HEIGHT + ((CELL_HEIGHT - GLYPH_HEIGHT) >> 1) - 1; + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + GX_Position2s16(x, y - GLYPH_HEIGHT); + GX_TexCoord2f32(tx / 256.f, ty / 128.f); + + GX_Position2s16(x + GLYPH_WIDTH, y - GLYPH_HEIGHT); + GX_TexCoord2f32((tx + CELL_WIDTH) / 256.f, ty / 128.f); + + GX_Position2s16(x + GLYPH_WIDTH, y); + GX_TexCoord2f32((tx + CELL_WIDTH) / 256.f, (ty + CELL_HEIGHT) / 128.f); + + GX_Position2s16(x, y); + GX_TexCoord2f32(tx / 256.f, (ty + CELL_HEIGHT) / 128.f); + GX_End(); + x += FONT_TRACKING; + } +} diff --git a/src/platform/wii/main.c b/src/platform/wii/main.c index 094ab42d9..a4898335a 100644 --- a/src/platform/wii/main.c +++ b/src/platform/wii/main.c @@ -16,6 +16,9 @@ #include "gba/serialize.h" #include "gba/supervisor/overrides.h" #include "gba/video.h" +#include "util/gui.h" +#include "util/gui/file-select.h" +#include "util/gui/font.h" #include "util/vfs.h" #define SAMPLES 1024 @@ -27,6 +30,10 @@ static bool GBAWiiLoadGame(const char* path); static void _postVideoFrame(struct GBAAVStream*, struct GBAVideoRenderer* renderer); static void _audioDMA(void); +static void _drawStart(void); +static void _drawEnd(void); +static int _pollInput(void); + static struct GBA gba; static struct ARMCore cpu; static struct GBAVideoSoftwareRenderer renderer; @@ -42,9 +49,11 @@ static GXTexObj tex; static void* framebuffer[2]; static int whichFb = 0; -static struct GBAStereoSample audioBuffer[2][SAMPLES] __attribute__ ((__aligned__(32))); -static size_t audioBufferSize = 0; -static int currentAudioBuffer = 0; +static struct GBAStereoSample audioBuffer[3][SAMPLES] __attribute__((__aligned__(32))); +static volatile size_t audioBufferSize = 0; +static volatile int currentAudioBuffer = 0; + +static struct GUIFont* font; int main() { VIDEO_Init(); @@ -108,8 +117,6 @@ int main() { GX_InvalidateTexAll(); Mtx44 proj; - guOrtho(proj, 0, VIDEO_VERTICAL_PIXELS, 0, VIDEO_HORIZONTAL_PIXELS, 0, 300); - GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC); guVector cam = { 0.0f, 0.0f, 0.0f }; guVector up = { 0.0f, 1.0f, 0.0f }; @@ -125,6 +132,8 @@ int main() { memset(texmem, 0, 256 * 256 * BYTES_PER_PIXEL); GX_InitTexObj(&tex, texmem, 256, 256, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE); + font = GUIFontCreate(); + fatInitDefault(); logfile = fopen("/mgba.log", "w"); @@ -154,16 +163,30 @@ int main() { blip_set_rates(gba.audio.right, GBA_ARM7TDMI_FREQUENCY, 48000); #endif - if (!GBAWiiLoadGame("/rom.gba")) { + char path[256]; + guOrtho(proj, 0, 240, 0, 400, 0, 300); + GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC); + + struct GUIParams params = { + 400, 240, + font, _drawStart, _drawEnd, _pollInput + }; + if (!selectFile(¶ms, "sd:", path, sizeof(path), "gba") || !GBAWiiLoadGame(path)) { + free(renderer.outputBuffer); + GUIFontDestroy(font); return 1; } + guOrtho(proj, 0, VIDEO_VERTICAL_PIXELS, 0, VIDEO_HORIZONTAL_PIXELS, 0, 300); + GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC); + while (true) { #if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF int available = blip_samples_avail(gba.audio.left); if (available + audioBufferSize > SAMPLES) { available = SAMPLES - audioBufferSize; } + available &= ~((32 / sizeof(struct GBAStereoSample)) - 1); // Force align to 32 bytes if (available > 0) { blip_read_samples(gba.audio.left, &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true); blip_read_samples(gba.audio.right, &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true); @@ -223,10 +246,15 @@ int main() { rom->close(rom); save->close(save); + free(renderer.outputBuffer); + GUIFontDestroy(font); + return 0; } static void GBAWiiFrame(void) { + VIDEO_WaitVSync(); + size_t x, y; uint64_t* texdest = (uint64_t*) texmem; uint64_t* texsrc = (uint64_t*) renderer.outputBuffer; @@ -240,10 +268,10 @@ static void GBAWiiFrame(void) { } DCFlushRange(texdest, 256 * 256 * BYTES_PER_PIXEL); - GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE); - GX_SetColorUpdate(GX_TRUE); + _drawStart(); - GX_SetViewport(0, 0, mode->fbWidth, mode->efbHeight, 0, 1); + GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_SET); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0); GX_InvalidateTexAll(); GX_LoadTexObj(&tex, GX_TEXMAP0); @@ -261,17 +289,14 @@ static void GBAWiiFrame(void) { GX_TexCoord2s16(0, 0); GX_End(); - GX_DrawDone(); - - whichFb = !whichFb; - - GX_CopyDisp(framebuffer[whichFb], GX_TRUE); - VIDEO_SetNextFramebuffer(framebuffer[whichFb]); - VIDEO_Flush(); - VIDEO_WaitVSync(); + _drawEnd(); } bool GBAWiiLoadGame(const char* path) { + _drawStart(); + GUIFontPrintf(font, 0, 30, GUI_TEXT_CENTER, 0xFFFFFFFF, "Loading..."); + _drawEnd(); + rom = VFileOpen(path, O_RDONLY); if (!rom) { @@ -281,7 +306,7 @@ bool GBAWiiLoadGame(const char* path) { return false; } - save = VFileOpen("test.sav", O_RDWR | O_CREAT); + save = VDirOptionalOpenFile(0, path, 0, ".sav", O_RDWR | O_CREAT); GBALoadROM(&gba, rom, save, path); @@ -314,8 +339,51 @@ static void _audioDMA(void) { if (!audioBufferSize) { return; } - currentAudioBuffer = !currentAudioBuffer; DCFlushRange(audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample)); AUDIO_InitDMA((u32) audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample)); + currentAudioBuffer = (currentAudioBuffer + 1) % 3; audioBufferSize = 0; } + +static void _drawStart(void) { + GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE); + GX_SetColorUpdate(GX_TRUE); + + GX_SetViewport(0, 0, mode->fbWidth, mode->efbHeight, 0, 1); +} + +static void _drawEnd(void) { + GX_DrawDone(); + + whichFb = !whichFb; + + GX_CopyDisp(framebuffer[whichFb], GX_TRUE); + VIDEO_SetNextFramebuffer(framebuffer[whichFb]); + VIDEO_Flush(); +} + +static int _pollInput(void) { + PAD_ScanPads(); + u16 padkeys = PAD_ButtonsHeld(0); + int keys = 0; + gba.keySource = &keys; + if (padkeys & PAD_BUTTON_A) { + keys |= 1 << GUI_INPUT_SELECT; + } + if (padkeys & PAD_BUTTON_B) { + keys |= 1 << GUI_INPUT_BACK; + } + if (padkeys & PAD_BUTTON_LEFT) { + keys |= 1 << GUI_INPUT_LEFT; + } + if (padkeys & PAD_BUTTON_RIGHT) { + keys |= 1 << GUI_INPUT_RIGHT; + } + if (padkeys & PAD_BUTTON_UP) { + keys |= 1 << GUI_INPUT_UP; + } + if (padkeys & PAD_BUTTON_DOWN) { + keys |= 1 << GUI_INPUT_DOWN; + } + return keys; +} diff --git a/src/util/gui/file-select.c b/src/util/gui/file-select.c index cc13a2b81..05a0fbb67 100644 --- a/src/util/gui/file-select.c +++ b/src/util/gui/file-select.c @@ -56,6 +56,7 @@ bool selectFile(const struct GUIParams* params, const char* basePath, char* outP strncpy(currentPath, basePath, sizeof(currentPath)); int oldInput = -1; size_t fileIndex = 0; + size_t start = 0; struct FileList currentFiles; FileListInit(¤tFiles, 0); @@ -72,6 +73,12 @@ bool selectFile(const struct GUIParams* params, const char* basePath, char* outP if (newInput & (1 << GUI_INPUT_DOWN) && fileIndex < FileListSize(¤tFiles) - 1) { ++fileIndex; } + if (fileIndex < start) { + start = fileIndex; + } + while ((fileIndex - start + 4) * GUIFontHeight(params->font) > params->height) { + ++start; + } if (newInput & (1 << GUI_INPUT_CANCEL)) { _cleanFiles(¤tFiles); FileListDeinit(¤tFiles); @@ -101,7 +108,7 @@ bool selectFile(const struct GUIParams* params, const char* basePath, char* outP GUIFontPrintf(params->font, 0, y, GUI_TEXT_LEFT, 0xFFFFFFFF, "Current directory: %s", currentPath); y += 2 * GUIFontHeight(params->font); size_t i; - for (i = 0; i < FileListSize(¤tFiles); ++i) { + for (i = start; i < FileListSize(¤tFiles); ++i) { int color = 0xE0A0A0A0; char bullet = ' '; if (i == fileIndex) {