diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e08466a7..71935705f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ 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 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) file(GLOB SIO_SRC ${CMAKE_SOURCE_DIR}/src/gba/sio/lockstep.c) file(GLOB THIRD_PARTY_SRC ${CMAKE_SOURCE_DIR}/src/third-party/inih/*.c) @@ -214,6 +215,10 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm.*") enable_language(ASM) endif() +if(PSP2) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-format") +endif() + include(CheckFunctionExists) check_function_exists(strdup HAVE_STRDUP) check_function_exists(strndup HAVE_STRNDUP) diff --git a/res/font.png b/res/font.png new file mode 100644 index 000000000..73b9893c5 Binary files /dev/null and b/res/font.png differ diff --git a/src/gba/hardware.c b/src/gba/hardware.c index 1ce2e18eb..cf00bb6f6 100644 --- a/src/gba/hardware.c +++ b/src/gba/hardware.c @@ -277,12 +277,8 @@ void _rtcUpdateClock(struct GBACartridgeHardware* hw) { t = time(0); } struct tm date; -#ifdef _WIN32 - date = *localtime(&t); -#elif defined(PSP2) - SceRtcTime scertc; - sceRtcGetCurrentClockLocalTime(&scertc); - sceRtcGetTime_t(&scertc, &t); +#if defined(_WIN32) || defined(PSP2) + localtime_s(&date, &t); #else localtime_r(&t, &date); #endif diff --git a/src/platform/psp2/CMakeLists.txt b/src/platform/psp2/CMakeLists.txt index 1736db4ba..605cffd83 100644 --- a/src/platform/psp2/CMakeLists.txt +++ b/src/platform/psp2/CMakeLists.txt @@ -1,8 +1,10 @@ file(GLOB PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/psp2/*.c) -set(PLATFORM_LIBRARY -lvita2d -lSceCtrl_stub -lSceRtc_stub -lSceGxm_stub -lSceDisplay_stub -lm_stub) +execute_process(COMMAND ${DEVKITARM}/bin/arm-none-eabi-objcopy -I binary -O elf32-littlearm -B arm font.png ${CMAKE_BINARY_DIR}/font.o WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/res) -add_executable(${BINARY_NAME}.elf ${PLATFORM_SRC}) +set(PLATFORM_LIBRARY -lvita2d -lSceCtrl_stub -lSceRtc_stub -lSceGxm_stub -lSceDisplay_stub -lSceAudio_stub -lpng -lz -lm_stub) + +add_executable(${BINARY_NAME}.elf ${PLATFORM_SRC} ${GUI_SRC} ${CMAKE_BINARY_DIR}/font.o) target_link_libraries(${BINARY_NAME}.elf ${BINARY_NAME} ${PLATFORM_LIBRARY}) set_target_properties(${BINARY_NAME}.elf PROPERTIES OUTPUT_NAME ${BINARY_NAME}.elf) add_custom_command(TARGET ${BINARY_NAME}.elf POST_BUILD COMMAND ${FIXUP} -q -S ${BINARY_NAME}.elf ${BINARY_NAME}.velf MAIN_DEPENDENCY ${BINARY_NAME}.elf) diff --git a/src/platform/psp2/CMakeToolchain.txt b/src/platform/psp2/CMakeToolchain.txt index e6490008b..da9bed0e5 100644 --- a/src/platform/psp2/CMakeToolchain.txt +++ b/src/platform/psp2/CMakeToolchain.txt @@ -14,8 +14,8 @@ set(toolchain_dir ${DEVKITARM}/psp2) set(toolchain_bin_dir ${toolchain_dir}/bin) set(cross_prefix ${DEVKITARM}/bin/arm-none-eabi-) set(inc_flags -I${toolchain_dir}/include) -set(arch_flags "-march=armv7-a -mtune=cortex-a9") -set(link_flags "-L${toolchain_dir}/lib -specs=psp2.specs ${arch_flags}") +set(arch_flags "-march=armv7-a -mtune=cortex-a9 -specs=psp2.specs") +set(link_flags "-L${toolchain_dir}/lib") set(CMAKE_SYSTEM_NAME Generic CACHE INTERNAL "system name") set(CMAKE_SYSTEM_PROCESSOR arm CACHE INTERNAL "processor") diff --git a/src/platform/psp2/gui-font.c b/src/platform/psp2/gui-font.c new file mode 100644 index 000000000..6dd6619d1 --- /dev/null +++ b/src/platform/psp2/gui-font.c @@ -0,0 +1,60 @@ +/* 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 + +#define GLYPH_HEIGHT 11 +#define GLYPH_WIDTH 14 +#define FONT_TRACKING 10 +#define CELL_HEIGHT 16 +#define CELL_WIDTH 16 + +extern const uint8_t _binary_font_png_start[]; + +struct GUIFont { + vita2d_texture* tex; +}; + +struct GUIFont* GUIFontCreate(void) { + struct GUIFont* font = malloc(sizeof(struct GUIFont)); + if (!font) { + return 0; + } + font->tex = vita2d_load_PNG_buffer(_binary_font_png_start); + return font; +} + +void GUIFontDestroy(struct GUIFont* font) { + vita2d_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; + } + vita2d_draw_texture_tint_part(font->tex, x, y - GLYPH_HEIGHT, + (c & 15) * CELL_WIDTH + ((CELL_WIDTH - GLYPH_WIDTH) >> 1), + (c >> 4) * CELL_HEIGHT + ((CELL_HEIGHT - GLYPH_HEIGHT) >> 1), + GLYPH_WIDTH, GLYPH_HEIGHT, color); + x += FONT_TRACKING; + } +} diff --git a/src/platform/psp2/main.c b/src/platform/psp2/main.c index 82a407c4f..02f1e6f6e 100644 --- a/src/platform/psp2/main.c +++ b/src/platform/psp2/main.c @@ -3,132 +3,84 @@ * 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 "psp2-context.h" -#include "gba/renderers/video-software.h" -#include "util/memory.h" -#include "util/vfs.h" -#include "platform/psp2/sce-vfs.h" +#include "util/gui.h" +#include "util/gui/font.h" +#include "util/gui/file-select.h" #include -#include -#include -#include -#include #include +#include #include PSP2_MODULE_INFO(0, 0, "mGBA"); -#define PSP2_HORIZONTAL_PIXELS 960 -#define PSP2_VERTICAL_PIXELS 544 +static void _drawStart(void) { + vita2d_start_drawing(); + vita2d_clear_screen(); +} + +static void _drawEnd(void) { + vita2d_end_drawing(); + vita2d_swap_buffers(); +} + +static int _pollInput(void) { + SceCtrlData pad; + sceCtrlPeekBufferPositive(0, &pad, 1); + int input = 0; + if (pad.buttons & PSP2_CTRL_TRIANGLE) { + input |= 1 << GUI_INPUT_CANCEL; + } + if (pad.buttons & PSP2_CTRL_CIRCLE) { + input |= 1 << GUI_INPUT_BACK; + } + if (pad.buttons & PSP2_CTRL_CROSS) { + input |= 1 << GUI_INPUT_SELECT; + } + + if (pad.buttons & PSP2_CTRL_UP) { + input |= 1 << GUI_INPUT_UP; + } + if (pad.buttons & PSP2_CTRL_DOWN) { + input |= 1 << GUI_INPUT_DOWN; + } + if (pad.buttons & PSP2_CTRL_LEFT) { + input |= 1 << GUI_INPUT_LEFT; + } + if (pad.buttons & PSP2_CTRL_RIGHT) { + input |= 1 << GUI_INPUT_RIGHT; + } + return input; +} int main() { printf("%s initializing", projectName); - bool running = true; - - struct GBAVideoSoftwareRenderer renderer; - GBAVideoSoftwareRendererCreate(&renderer); - - struct GBA* gba = anonymousMemoryMap(sizeof(struct GBA)); - struct ARMCore* cpu = anonymousMemoryMap(sizeof(struct ARMCore)); - - printf("GBA: %08X", gba); - printf("CPU: %08X", cpu); - int activeKeys = 0; vita2d_init(); - vita2d_texture* tex = vita2d_create_empty_texture(256, 256); + struct GUIFont* font = GUIFontCreate(); + GBAPSP2Setup(); + struct GUIParams params = { + PSP2_HORIZONTAL_PIXELS, PSP2_VERTICAL_PIXELS, + font, _drawStart, _drawEnd, _pollInput + }; - renderer.outputBuffer = vita2d_texture_get_datap(tex); - renderer.outputBufferStride = 256; - - struct VFile* rom = VFileOpenSce("cache0:/VitaDefilerClient/Documents/GBA/rom.gba", PSP2_O_RDONLY, 0666); - struct VFile* save = VFileOpenSce("cache0:/VitaDefilerClient/Documents/GBA/rom.sav", PSP2_O_RDWR | PSP2_O_CREAT, 0666); - - printf("ROM: %08X", rom); - printf("Save: %08X", save); - - GBACreate(gba); - ARMSetComponents(cpu, &gba->d, 0, 0); - ARMInit(cpu); - printf("%s initialized.", "CPU"); - - gba->keySource = &activeKeys; - gba->sync = 0; - - GBAVideoAssociateRenderer(&gba->video, &renderer.d); - - GBALoadROM(gba, rom, save, 0); - printf("%s loaded.", "ROM"); - - ARMReset(cpu); - - printf("%s all set and ready to roll.", projectName); - - int frameCounter = 0; - while (running) { - ARMRunLoop(cpu); - - if (frameCounter != gba->video.frameCounter) { - SceCtrlData pad; - sceCtrlPeekBufferPositive(0, &pad, 1); - activeKeys = 0; - if (pad.buttons & PSP2_CTRL_CROSS) { - activeKeys |= 1 << GBA_KEY_A; - } - if (pad.buttons & PSP2_CTRL_CIRCLE) { - activeKeys |= 1 << GBA_KEY_B; - } - if (pad.buttons & PSP2_CTRL_START) { - activeKeys |= 1 << GBA_KEY_START; - } - if (pad.buttons & PSP2_CTRL_SELECT) { - activeKeys |= 1 << GBA_KEY_SELECT; - } - if (pad.buttons & PSP2_CTRL_UP) { - activeKeys |= 1 << GBA_KEY_UP; - } - if (pad.buttons & PSP2_CTRL_DOWN) { - activeKeys |= 1 << GBA_KEY_DOWN; - } - if (pad.buttons & PSP2_CTRL_LEFT) { - activeKeys |= 1 << GBA_KEY_LEFT; - } - if (pad.buttons & PSP2_CTRL_RIGHT) { - activeKeys |= 1 << GBA_KEY_RIGHT; - } - if (pad.buttons & PSP2_CTRL_LTRIGGER) { - activeKeys |= 1 << GBA_KEY_L; - } - if (pad.buttons & PSP2_CTRL_RTRIGGER) { - activeKeys |= 1 << GBA_KEY_R; - } - - vita2d_start_drawing(); - vita2d_clear_screen(); - vita2d_draw_texture_scale(tex, 120, 32, 3.0f, 3.0f); - vita2d_end_drawing(); - vita2d_swap_buffers(); - - frameCounter = gba->video.frameCounter; + while (true) { + char path[256]; + if (!selectFile(¶ms, "cache0:", path, sizeof(path), "gba")) { + break; } + GBAPSP2LoadROM(path); + GBAPSP2Runloop(); + GBAPSP2UnloadROM(); } - printf("%s shutting down...", projectName); - ARMDeinit(cpu); - GBADestroy(gba); - - rom->close(rom); - save->close(save); - - mappedMemoryFree(gba, 0); - mappedMemoryFree(cpu, 0); + GBAPSP2Teardown(); + GUIFontDestroy(font); vita2d_fini(); - vita2d_free_texture(tex); sceKernelExitProcess(0); return 0; diff --git a/src/platform/psp2/memory.c b/src/platform/psp2/memory.c index 6f06134e1..8867c5d74 100644 --- a/src/platform/psp2/memory.c +++ b/src/platform/psp2/memory.c @@ -6,7 +6,7 @@ #include "util/memory.h" #include "util/vector.h" -#include +#include #include DECLARE_VECTOR(SceUIDList, SceUID); diff --git a/src/platform/psp2/psp2-common.h b/src/platform/psp2/psp2-common.h new file mode 100644 index 000000000..ac00298e5 --- /dev/null +++ b/src/platform/psp2/psp2-common.h @@ -0,0 +1,14 @@ +/* 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/. */ +#ifndef PSP2_COMMON_H +#define PSP2_COMMON_H + +#include "util/common.h" + +#define PSP2_HORIZONTAL_PIXELS 960 +#define PSP2_VERTICAL_PIXELS 544 + +#endif diff --git a/src/platform/psp2/psp2-context.c b/src/platform/psp2/psp2-context.c new file mode 100644 index 000000000..a4ccea6de --- /dev/null +++ b/src/platform/psp2/psp2-context.c @@ -0,0 +1,259 @@ +/* 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 "psp2-context.h" + +#include "gba/gba.h" +#include "gba/input.h" +#include "gba/audio.h" +#include "gba/supervisor/overrides.h" +#include "gba/video.h" + +#include "gba/renderers/video-software.h" +#include "util/circle-buffer.h" +#include "util/memory.h" +#include "util/threading.h" +#include "util/vfs.h" +#include "platform/psp2/sce-vfs.h" +#include "third-party/blip_buf/blip_buf.h" + +#include +#include +#include +#include +#include + +#include + +static char gameName[13]; +static struct GBA* gba; +static struct ARMCore* cpu; +static struct VFile* rom; +static struct VFile* save; +static struct GBAVideoSoftwareRenderer renderer; +static struct GBAInputMap inputMap; +static vita2d_texture* tex; +static Thread audioThread; + +static bool fullscreen = false; + +#define PSP2_INPUT 0x50535032 +#define PSP2_SAMPLES 64 +#define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 19) + +static struct GBAPSP2AudioContext { + struct CircleBuffer buffer; + Mutex mutex; + Condition cond; + bool running; +} audioContext; + +static void _mapVitaKey(struct GBAInputMap* map, int pspKey, enum GBAKey key) { + GBAInputBindKey(map, PSP2_INPUT, __builtin_ctz(pspKey), key); +} + +static THREAD_ENTRY _audioThread(void* context) { + struct GBAPSP2AudioContext* audio = (struct GBAPSP2AudioContext*) context; + struct GBAStereoSample buffer[PSP2_AUDIO_BUFFER_SIZE]; + int audioPort = sceAudioOutOpenPort(PSP2_AUDIO_OUT_PORT_TYPE_MAIN, PSP2_AUDIO_BUFFER_SIZE, 48000, PSP2_AUDIO_OUT_MODE_STEREO); + while (audio->running) { + MutexLock(&audio->mutex); + int len = CircleBufferSize(&audio->buffer); + len /= sizeof(buffer[0]); + if (len > PSP2_AUDIO_BUFFER_SIZE) { + len = PSP2_AUDIO_BUFFER_SIZE; + } + if (len > 0) { + len &= ~(PSP2_AUDIO_MIN_LEN - 1); + CircleBufferRead(&audio->buffer, buffer, len * sizeof(buffer[0])); + MutexUnlock(&audio->mutex); + sceAudioOutSetConfig(audioPort, len, -1, -1); + sceAudioOutOutput(audioPort, buffer); + MutexLock(&audio->mutex); + } + ConditionWait(&audio->cond, &audio->mutex); + MutexUnlock(&audio->mutex); + } + sceAudioOutReleasePort(audioPort); + return 0; +} + +void GBAPSP2Setup() { + GBAInputMapInit(&inputMap); + _mapVitaKey(&inputMap, PSP2_CTRL_CROSS, GBA_KEY_A); + _mapVitaKey(&inputMap, PSP2_CTRL_CIRCLE, GBA_KEY_B); + _mapVitaKey(&inputMap, PSP2_CTRL_START, GBA_KEY_START); + _mapVitaKey(&inputMap, PSP2_CTRL_SELECT, GBA_KEY_SELECT); + _mapVitaKey(&inputMap, PSP2_CTRL_UP, GBA_KEY_UP); + _mapVitaKey(&inputMap, PSP2_CTRL_DOWN, GBA_KEY_DOWN); + _mapVitaKey(&inputMap, PSP2_CTRL_LEFT, GBA_KEY_LEFT); + _mapVitaKey(&inputMap, PSP2_CTRL_RIGHT, GBA_KEY_RIGHT); + _mapVitaKey(&inputMap, PSP2_CTRL_LTRIGGER, GBA_KEY_L); + _mapVitaKey(&inputMap, PSP2_CTRL_RTRIGGER, GBA_KEY_R); + + struct GBAAxis desc = { GBA_KEY_DOWN, GBA_KEY_UP, 192, 64 }; + GBAInputBindAxis(&inputMap, PSP2_INPUT, 0, &desc); + desc = (struct GBAAxis) { GBA_KEY_RIGHT, GBA_KEY_LEFT, 192, 64 }; + GBAInputBindAxis(&inputMap, PSP2_INPUT, 1, &desc); + + tex = vita2d_create_empty_texture_format(256, 256, SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR); + + renderer.outputBuffer = vita2d_texture_get_datap(tex); + renderer.outputBufferStride = 256; +} + +void GBAPSP2LoadROM(const char* path) { + GBAVideoSoftwareRendererCreate(&renderer); + + gba = anonymousMemoryMap(sizeof(struct GBA)); + cpu = anonymousMemoryMap(sizeof(struct ARMCore)); + + printf("GBA: %08X", gba); + printf("CPU: %08X", cpu); + + rom = VFileOpenSce(path, PSP2_O_RDONLY, 0666); + save = VDirOptionalOpenFile(0, path, 0, ".sav", PSP2_O_RDWR | PSP2_O_CREAT); + + printf("ROM: %08X", rom); + printf("Save: %08X", save); + + GBACreate(gba); + ARMSetComponents(cpu, &gba->d, 0, 0); + ARMInit(cpu); + printf("%s initialized.", "CPU"); + + gba->sync = 0; + + GBAVideoAssociateRenderer(&gba->video, &renderer.d); + + GBALoadROM(gba, rom, save, 0); + GBAOverrideApplyDefaults(gba); + GBAGetGameTitle(gba, gameName); + printf("ROM loaded: %s", gameName); + + ARMReset(cpu); + double ratio = GBAAudioCalculateRatio(1, 60, 1); + blip_set_rates(gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio); + blip_set_rates(gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio); + + CircleBufferInit(&audioContext.buffer, PSP2_AUDIO_BUFFER_SIZE * sizeof(struct GBAStereoSample)); + MutexInit(&audioContext.mutex); + ConditionInit(&audioContext.cond); + audioContext.running = true; + ThreadCreate(&audioThread, _audioThread, &audioContext); + + printf("%s all set and ready to roll.", projectName); +} + +void GBAPSP2Runloop(void) { + int activeKeys = 0; + gba->keySource = &activeKeys; + + bool fsToggle = false; + int frameCounter = 0; + while (true) { + ARMRunLoop(cpu); + + if (frameCounter != gba->video.frameCounter) { + SceCtrlData pad; + sceCtrlPeekBufferPositive(0, &pad, 1); + if (pad.buttons & PSP2_CTRL_TRIANGLE) { + break; + } + if (pad.buttons & PSP2_CTRL_SQUARE) { + if (!fsToggle) { + fullscreen = !fullscreen; + } + fsToggle = true; + } else { + fsToggle = false; + } + + activeKeys = GBAInputMapKeyBits(&inputMap, PSP2_INPUT, pad.buttons, 0); + enum GBAKey angles = GBAInputMapAxis(&inputMap, PSP2_INPUT, 0, pad.ly); + if (angles != GBA_KEY_NONE) { + activeKeys |= 1 << angles; + } + angles = GBAInputMapAxis(&inputMap, PSP2_INPUT, 1, pad.lx); + if (angles != GBA_KEY_NONE) { + activeKeys |= 1 << angles; + } + angles = GBAInputMapAxis(&inputMap, PSP2_INPUT, 2, pad.ry); + if (angles != GBA_KEY_NONE) { + activeKeys |= 1 << angles; + } + angles = GBAInputMapAxis(&inputMap, PSP2_INPUT, 3, pad.rx); + if (angles != GBA_KEY_NONE) { + activeKeys |= 1 << angles; + } + + MutexLock(&audioContext.mutex); + while (blip_samples_avail(gba->audio.left) >= PSP2_SAMPLES) { + if (CircleBufferSize(&audioContext.buffer) + PSP2_SAMPLES * sizeof(struct GBAStereoSample) > CircleBufferCapacity(&audioContext.buffer)) { + break; + } + struct GBAStereoSample samples[PSP2_SAMPLES]; + blip_read_samples(gba->audio.left, &samples[0].left, PSP2_SAMPLES, true); + blip_read_samples(gba->audio.right, &samples[0].right, PSP2_SAMPLES, true); + int i; + for (i = 0; i < PSP2_SAMPLES; ++i) { + CircleBufferWrite16(&audioContext.buffer, samples[i].left); + CircleBufferWrite16(&audioContext.buffer, samples[i].right); + } + } + ConditionWake(&audioContext.cond); + MutexUnlock(&audioContext.mutex); + + vita2d_start_drawing(); + vita2d_clear_screen(); + GBAPSP2Draw(); + vita2d_end_drawing(); + vita2d_swap_buffers(); + + frameCounter = gba->video.frameCounter; + } + } +} + +void GBAPSP2UnloadROM(void) { + printf("%s shutting down...", projectName); + + ARMDeinit(cpu); + GBADestroy(gba); + + rom->close(rom); + save->close(save); + + MutexLock(&audioContext.mutex); + audioContext.running = false; + ConditionWake(&audioContext.cond); + MutexUnlock(&audioContext.mutex); + ThreadJoin(audioThread); + CircleBufferDeinit(&audioContext.buffer); + MutexDeinit(&audioContext.mutex); + ConditionDeinit(&audioContext.cond); + + mappedMemoryFree(gba, 0); + mappedMemoryFree(cpu, 0); + + gba = 0; + cpu = 0; + rom = 0; + save = 0; +} + +void GBAPSP2Teardown(void) { + GBAInputMapDeinit(&inputMap); + + vita2d_free_texture(tex); +} + +void GBAPSP2Draw(void) { + if (fullscreen) { + vita2d_draw_texture_scale(tex, 0, 0, 960.0f / 240.0f, 544.0f / 160.0f); + } else { + vita2d_draw_texture_scale(tex, 120, 32, 3.0f, 3.0f); + } +} diff --git a/src/platform/psp2/psp2-context.h b/src/platform/psp2/psp2-context.h new file mode 100644 index 000000000..45fca3af6 --- /dev/null +++ b/src/platform/psp2/psp2-context.h @@ -0,0 +1,20 @@ +/* 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/. */ +#ifndef PSP2_CONTEXT_H +#define PSP2_CONTEXT_H + +#include "psp2-common.h" + +void GBAPSP2Setup(void); +void GBAPSP2Teardown(void); + +void GBAPSP2LoadROM(const char* path); +void GBAPSP2Runloop(void); +void GBAPSP2UnloadROM(void); + +void GBAPSP2Draw(void); + +#endif diff --git a/src/platform/psp2/sce-vfs.c b/src/platform/psp2/sce-vfs.c index 1ba99640e..becb34230 100644 --- a/src/platform/psp2/sce-vfs.c +++ b/src/platform/psp2/sce-vfs.c @@ -5,16 +5,29 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "sce-vfs.h" +#include + #include "util/vfs.h" #include "util/memory.h" - struct VFileSce { struct VFile d; SceUID fd; }; +struct VDirEntrySce { + struct VDirEntry d; + SceIoDirent ent; +}; + +struct VDirSce { + struct VDir d; + struct VDirEntrySce de; + SceUID fd; + char* path; +}; + static bool _vfsceClose(struct VFile* vf); static off_t _vfsceSeek(struct VFile* vf, off_t offset, int whence); static ssize_t _vfsceRead(struct VFile* vf, void* buffer, size_t size); @@ -23,6 +36,14 @@ static void* _vfsceMap(struct VFile* vf, size_t size, int flags); static void _vfsceUnmap(struct VFile* vf, void* memory, size_t size); static void _vfsceTruncate(struct VFile* vf, size_t size); static ssize_t _vfsceSize(struct VFile* vf); +static bool _vfsceSync(struct VFile* vf, const void* memory, size_t size); + +static bool _vdsceClose(struct VDir* vd); +static void _vdsceRewind(struct VDir* vd); +static struct VDirEntry* _vdsceListNext(struct VDir* vd); +static struct VFile* _vdsceOpenFile(struct VDir* vd, const char* path, int mode); + +static const char* _vdesceName(struct VDirEntry* vde); struct VFile* VFileOpenSce(const char* path, int flags, SceMode mode) { struct VFileSce* vfsce = malloc(sizeof(struct VFileSce)); @@ -45,6 +66,7 @@ struct VFile* VFileOpenSce(const char* path, int flags, SceMode mode) { vfsce->d.unmap = _vfsceUnmap; vfsce->d.truncate = _vfsceTruncate; vfsce->d.size = _vfsceSize; + vfsce->d.sync = _vfsceSync; return &vfsce->d; } @@ -97,3 +119,77 @@ ssize_t _vfsceSize(struct VFile* vf) { sceIoLseek(vfsce->fd, cur, SEEK_SET); return end; } + +bool _vfsceSync(struct VFile* vf, const void* buffer, size_t size) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + if (buffer && size) { + SceOff cur = sceIoLseek(vfsce->fd, 0, SEEK_CUR); + sceIoLseek(vfsce->fd, 0, SEEK_SET); + sceIoWrite(vfsce->fd, buffer, size); + sceIoLseek(vfsce->fd, cur, SEEK_SET); + } + // TODO: Get the right device + return sceIoSync("cache0:", 0) >= 0; +} + +struct VDir* VDirOpen(const char* path) { + SceUID dir = sceIoDopen(path); + if (dir < 0) { + return 0; + } + + struct VDirSce* vd = malloc(sizeof(struct VDirSce)); + vd->fd = dir; + vd->d.close = _vdsceClose; + vd->d.rewind = _vdsceRewind; + vd->d.listNext = _vdsceListNext; + vd->d.openFile = _vdsceOpenFile; + vd->path = strdup(path); + + vd->de.d.name = _vdesceName; + + return &vd->d; +} + +bool _vdsceClose(struct VDir* vd) { + struct VDirSce* vdsce = (struct VDirSce*) vd; + if (sceIoDclose(vdsce->fd) < 0) { + return false; + } + free(vdsce->path); + free(vdsce); + return true; +} + +void _vdsceRewind(struct VDir* vd) { + struct VDirSce* vdsce = (struct VDirSce*) vd; + sceIoDclose(vdsce->fd); + vdsce->fd = sceIoDopen(vdsce->path); +} + +struct VDirEntry* _vdsceListNext(struct VDir* vd) { + struct VDirSce* vdsce = (struct VDirSce*) vd; + if (sceIoDread(vdsce->fd, &vdsce->de.ent) <= 0) { + return 0; + } + return &vdsce->de.d; +} + +struct VFile* _vdsceOpenFile(struct VDir* vd, const char* path, int mode) { + struct VDirSce* vdsce = (struct VDirSce*) vd; + if (!path) { + return 0; + } + const char* dir = vdsce->path; + char* combined = malloc(sizeof(char) * (strlen(path) + strlen(dir) + 2)); + sprintf(combined, "%s%s%s", dir, PATH_SEP, path); + + struct VFile* file = VFileOpenSce(combined, mode, 0666); + free(combined); + return file; +} + +static const char* _vdesceName(struct VDirEntry* vde) { + struct VDirEntrySce* vdesce = (struct VDirEntrySce*) vde; + return vdesce->ent.d_name; +} diff --git a/src/platform/psp2/threading.h b/src/platform/psp2/threading.h new file mode 100644 index 000000000..4ca74af13 --- /dev/null +++ b/src/platform/psp2/threading.h @@ -0,0 +1,138 @@ +/* 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/. */ +#ifndef SCE_THREADING_H +#define SCE_THREADING_H + +#include + +typedef SceUID Thread; +typedef SceUID Mutex; +typedef struct { + Mutex mutex; + SceUID semaphore; + int waiting; +} Condition; +#define THREAD_ENTRY int +typedef THREAD_ENTRY (*ThreadEntry)(void*); + +static inline int MutexInit(Mutex* mutex) { + Mutex id = sceKernelCreateMutex("mutex", 0, 0, 0); + if (id < 0) { + return id; + } + *mutex = id; + return 0; +} + +static inline int MutexDeinit(Mutex* mutex) { + return sceKernelDeleteMutex(*mutex); +} + +static inline int MutexLock(Mutex* mutex) { + return sceKernelLockMutex(*mutex, 1, 0); +} + +static inline int MutexUnlock(Mutex* mutex) { + return sceKernelUnlockMutex(*mutex, 1); +} + +static inline int ConditionInit(Condition* cond) { + int res = MutexInit(&cond->mutex); + if (res < 0) { + return res; + } + cond->semaphore = sceKernelCreateSema("SceCondSema", 0, 0, 1, 0); + if (cond->semaphore < 0) { + MutexDeinit(&cond->mutex); + res = cond->semaphore; + } + cond->waiting = 0; + return res; +} + +static inline int ConditionDeinit(Condition* cond) { + MutexDeinit(&cond->mutex); + return sceKernelDeleteSema(cond->semaphore); +} + +static inline int ConditionWait(Condition* cond, Mutex* mutex) { + int ret = MutexLock(&cond->mutex); + if (ret < 0) { + return ret; + } + ++cond->waiting; + MutexUnlock(mutex); + MutexUnlock(&cond->mutex); + ret = sceKernelWaitSema(cond->semaphore, 1, 0); + if (ret < 0) { + printf("Premature wakeup: %08X", ret); + } + MutexLock(mutex); + return ret; +} + +static inline int ConditionWaitTimed(Condition* cond, Mutex* mutex, int32_t timeoutMs) { + int ret = MutexLock(&cond->mutex); + if (ret < 0) { + return ret; + } + ++cond->waiting; + MutexUnlock(mutex); + MutexUnlock(&cond->mutex); + SceUInt timeout = 0; + if (timeoutMs > 0) { + timeout = timeoutMs; + } + ret = sceKernelWaitSema(cond->semaphore, 1, &timeout); + if (ret < 0) { + printf("Premature wakeup: %08X", ret); + } + MutexLock(mutex); + return ret; +} + +static inline int ConditionWake(Condition* cond) { + MutexLock(&cond->mutex); + if (cond->waiting) { + --cond->waiting; + sceKernelSignalSema(cond->semaphore, 1); + } + MutexUnlock(&cond->mutex); + return 0; +} + +struct SceThreadEntryArgs { + void* context; + ThreadEntry entry; +}; + +static inline int _sceThreadEntry(SceSize args, void* argp) { + UNUSED(args); + struct SceThreadEntryArgs* arg = argp; + return arg->entry(arg->context); +} + +static inline int ThreadCreate(Thread* thread, ThreadEntry entry, void* context) { + Thread id = sceKernelCreateThread("SceThread", _sceThreadEntry, 0x40, 0x10000, 0, 0x70000, 0); + if (id < 0) { + *thread = 0; + return id; + } + *thread = id; + struct SceThreadEntryArgs args = { context, entry }; + sceKernelStartThread(id, sizeof(args), &args); + return 0; +} + +static inline int ThreadJoin(Thread thread) { + return sceKernelWaitThreadEnd(thread, 0, 0); +} + +static inline int ThreadSetName(const char* name) { + UNUSED(name); + return -1; +} +#endif diff --git a/src/third-party/inih/ini.c b/src/third-party/inih/ini.c index 56426cd06..8c06622ea 100755 --- a/src/third-party/inih/ini.c +++ b/src/third-party/inih/ini.c @@ -8,7 +8,9 @@ http://code.google.com/p/inih/ */ #include +#ifndef PSP2 #include +#endif #include #include "ini.h" diff --git a/src/util/arm-algo.S b/src/util/arm-algo.S index 38eaccddc..084ab39f9 100644 --- a/src/util/arm-algo.S +++ b/src/util/arm-algo.S @@ -3,7 +3,7 @@ # 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/. -#ifdef __ARM_NEON +#if defined(__ARM_NEON) && !defined(PSP2) # r0: Destination # r1: Source # r2: Number of words to copy as halfwords diff --git a/src/util/gui.h b/src/util/gui.h new file mode 100644 index 000000000..d8b2bb058 --- /dev/null +++ b/src/util/gui.h @@ -0,0 +1,35 @@ +/* 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/. */ +#ifndef GUI_H +#define GUI_H + +#include "util/common.h" + +struct GUIFont; + +enum GUIInput { + GUI_INPUT_NONE = -1, + GUI_INPUT_SELECT = 0, + GUI_INPUT_BACK, + GUI_INPUT_CANCEL, + + GUI_INPUT_UP, + GUI_INPUT_DOWN, + GUI_INPUT_LEFT, + GUI_INPUT_RIGHT, +}; + +struct GUIParams { + int width; + int height; + const struct GUIFont* font; + + void (*drawStart)(void); + void (*drawEnd)(void); + int (*pollInput)(void); +}; + +#endif diff --git a/src/util/gui/file-select.c b/src/util/gui/file-select.c new file mode 100644 index 000000000..cc13a2b81 --- /dev/null +++ b/src/util/gui/file-select.c @@ -0,0 +1,121 @@ +/* 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 "file-select.h" + +#include "util/gui/font.h" +#include "util/vector.h" +#include "util/vfs.h" + +DECLARE_VECTOR(FileList, char*); +DEFINE_VECTOR(FileList, char*); + +void _cleanFiles(struct FileList* currentFiles) { + size_t size = FileListSize(currentFiles); + size_t i; + for (i = 0; i < size; ++i) { + free(*FileListGetPointer(currentFiles, i)); + } + FileListClear(currentFiles); +} + +void _upDirectory(char* currentPath) { + char* end = strrchr(currentPath, '/'); + if (!end) { + return; + } + end[0] = '\0'; + if (end[1]) { + return; + } + // TODO: What if there was a trailing slash? +} + +bool _refreshDirectory(const char* currentPath, struct FileList* currentFiles) { + _cleanFiles(currentFiles); + + struct VDir* dir = VDirOpen(currentPath); + if (!dir) { + return false; + } + struct VDirEntry* de; + while ((de = dir->listNext(dir))) { + if (de->name(de)[0] == '.') { + continue; + } + *FileListAppend(currentFiles) = strdup(de->name(de)); + } + dir->close(dir); + return true; +} + +bool selectFile(const struct GUIParams* params, const char* basePath, char* outPath, size_t outLen, const char* suffix) { + char currentPath[256]; + strncpy(currentPath, basePath, sizeof(currentPath)); + int oldInput = -1; + size_t fileIndex = 0; + + struct FileList currentFiles; + FileListInit(¤tFiles, 0); + _refreshDirectory(currentPath, ¤tFiles); + + while (true) { + int input = params->pollInput(); + int newInput = input & (oldInput ^ input); + oldInput = input; + + if (newInput & (1 << GUI_INPUT_UP) && fileIndex > 0) { + --fileIndex; + } + if (newInput & (1 << GUI_INPUT_DOWN) && fileIndex < FileListSize(¤tFiles) - 1) { + ++fileIndex; + } + if (newInput & (1 << GUI_INPUT_CANCEL)) { + _cleanFiles(¤tFiles); + FileListDeinit(¤tFiles); + return false; + } + if (newInput & (1 << GUI_INPUT_SELECT)) { + snprintf(currentPath, sizeof(currentPath), "%s%c%s", currentPath, '/', *FileListGetPointer(¤tFiles, fileIndex)); + if (!_refreshDirectory(currentPath, ¤tFiles)) { + strncpy(outPath, currentPath, outLen); + return true; + } + fileIndex = 0; + } + if (newInput & (1 << GUI_INPUT_BACK)) { + if (strncmp(currentPath, basePath, sizeof(currentPath)) == 0) { + _cleanFiles(¤tFiles); + FileListDeinit(¤tFiles); + return false; + } + _upDirectory(currentPath); + _refreshDirectory(currentPath, ¤tFiles); + fileIndex = 0; + } + + params->drawStart(); + int y = GUIFontHeight(params->font); + 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) { + int color = 0xE0A0A0A0; + char bullet = ' '; + if (i == fileIndex) { + color = 0xFFFFFFFF; + bullet = '>'; + } + GUIFontPrintf(params->font, 0, y, GUI_TEXT_LEFT, color, "%c %s", bullet, *FileListGetPointer(¤tFiles, i)); + y += GUIFontHeight(params->font); + if (y + GUIFontHeight(params->font) > params->height) { + break; + } + } + y += GUIFontHeight(params->font) * 2; + + params->drawEnd(); + } +} diff --git a/src/util/gui/file-select.h b/src/util/gui/file-select.h new file mode 100644 index 000000000..c96f821c0 --- /dev/null +++ b/src/util/gui/file-select.h @@ -0,0 +1,13 @@ +/* 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/. */ +#ifndef GUI_FILE_CHOOSER_H +#define GUI_FILE_CHOOSER_H + +#include "util/gui.h" + +bool selectFile(const struct GUIParams*, const char* basePath, char* outPath, size_t outLen, const char* suffix); + +#endif diff --git a/src/util/gui/font.h b/src/util/gui/font.h new file mode 100644 index 000000000..4120814c2 --- /dev/null +++ b/src/util/gui/font.h @@ -0,0 +1,25 @@ +/* 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/. */ +#ifndef GUI_FONT_H +#define GUI_FONT_H + +#include "util/common.h" + +struct GUIFont; +struct GUIFont* GUIFontCreate(void); +void GUIFontDestroy(struct GUIFont*); + +enum GUITextAlignment { + GUI_TEXT_LEFT = 0, + GUI_TEXT_CENTER, + GUI_TEXT_RIGHT +}; + +int GUIFontHeight(const struct GUIFont*); + +void GUIFontPrintf(const struct GUIFont*, int x, int y, enum GUITextAlignment, uint32_t color, const char* text, ...); + +#endif diff --git a/src/util/threading.h b/src/util/threading.h index b65708720..a4404927d 100644 --- a/src/util/threading.h +++ b/src/util/threading.h @@ -13,6 +13,8 @@ #include "platform/posix/threading.h" #elif _WIN32 #include "platform/windows/threading.h" +#elif PSP2 +#include "platform/psp2/threading.h" #else #define DISABLE_THREADING #endif