Merge branch 'port/psp2'
jump to
@@ -183,7 +183,7 @@ add_definitions(-D_DARWIN_C_SOURCE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.6") endif() -if(NOT HAIKU AND NOT MSVC) +if(NOT HAIKU AND NOT MSVC AND NOT PSP2) list(APPEND OS_LIB m) endif()@@ -213,6 +213,10 @@ endif()
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm.*") enable_language(ASM) +endif() + +if(PSP2) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-format") endif() include(CheckFunctionExists)@@ -451,6 +455,10 @@ endif()
if(WII) add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/wii ${CMAKE_BINARY_DIR}) +endif() + +if(PSP2) + add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/psp2 ${CMAKE_BINARY_DIR}/psp2) endif() if(BUILD_PERF)
@@ -277,8 +277,8 @@ } else {
t = time(0); } struct tm date; -#ifdef _WIN32 - date = *localtime(&t); +#if defined(_WIN32) || defined(PSP2) + localtime_s(&date, &t); #else localtime_r(&t, &date); #endif
@@ -18,6 +18,10 @@ #include <shlobj.h>
#include <strsafe.h> #endif +#ifdef PSP2 +#include <psp2/io/stat.h> +#endif + #define SECTION_NAME_MAX 128 static const char* _lookupValue(const struct GBAConfig* config, const char* key) {@@ -135,13 +139,8 @@ return ConfigurationWrite(&config->configTable, path);
} void GBAConfigMakePortable(const struct GBAConfig* config) { - struct VFile* portable; -#ifndef _WIN32 - char out[PATH_MAX]; - getcwd(out, PATH_MAX); - strncat(out, PATH_SEP "portable.ini", PATH_MAX - strlen(out)); - portable = VFileOpen(out, O_WRONLY | O_CREAT); -#else + struct VFile* portable = 0; +#ifdef _WIN32 char out[MAX_PATH]; wchar_t wpath[MAX_PATH]; wchar_t wprojectName[MAX_PATH];@@ -152,6 +151,13 @@ PathRemoveFileSpecW(wpath);
WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, MAX_PATH, 0, 0); StringCchCatA(out, MAX_PATH, "\\portable.ini"); portable = VFileOpen(out, O_WRONLY | O_CREAT); +#elif defined(PSP2) + // Already portable +#else + char out[PATH_MAX]; + getcwd(out, PATH_MAX); + strncat(out, PATH_SEP "portable.ini", PATH_MAX - strlen(out)); + portable = VFileOpen(out, O_WRONLY | O_CREAT); #endif if (portable) { portable->close(portable);@@ -161,22 +167,7 @@ }
void GBAConfigDirectory(char* out, size_t outLength) { struct VFile* portable; -#ifndef _WIN32 - getcwd(out, outLength); - strncat(out, PATH_SEP "portable.ini", outLength - strlen(out)); - portable = VFileOpen(out, O_RDONLY); - if (portable) { - getcwd(out, outLength); - portable->close(portable); - return; - } - - char* home = getenv("HOME"); - snprintf(out, outLength, "%s/.config", home); - mkdir(out, 0755); - snprintf(out, outLength, "%s/.config/%s", home, binaryName); - mkdir(out, 0755); -#else +#ifdef _WIN32 wchar_t wpath[MAX_PATH]; wchar_t wprojectName[MAX_PATH]; MultiByteToWideChar(CP_UTF8, 0, projectName, -1, wprojectName, MAX_PATH);@@ -196,6 +187,25 @@ CoTaskMemFree(home);
CreateDirectoryW(wpath, NULL); } WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, outLength, 0, 0); +#elif defined(PSP2) + UNUSED(portable); + snprintf(out, outLength, "cache0:/%s", binaryName); + sceIoMkdir(out, 0777); +#else + getcwd(out, outLength); + strncat(out, PATH_SEP "portable.ini", outLength - strlen(out)); + portable = VFileOpen(out, O_RDONLY); + if (portable) { + getcwd(out, outLength); + portable->close(portable); + return; + } + + char* home = getenv("HOME"); + snprintf(out, outLength, "%s/.config", home); + mkdir(out, 0755); + snprintf(out, outLength, "%s/.config/%s", home, binaryName); + mkdir(out, 0755); #endif }
@@ -0,0 +1,11 @@
+file(GLOB PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/psp2/*.c) +include_directories(${CMAKE_BINARY_DIR}) + +execute_process(COMMAND ${RAW2C} ${CMAKE_SOURCE_DIR}/res/font.png) + +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.c) +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)
@@ -0,0 +1,53 @@
+if(DEFINED ENV{DEVKITPRO}) + set(DEVKITPRO $ENV{DEVKITPRO}) +else() + message(FATAL_ERROR "Could not find DEVKITPRO in environment") +endif() + +if(DEFINED ENV{DEVKITARM}) + set(DEVKITARM $ENV{DEVKITARM}) +else() + set(DEVKITARM ${DEVKITPRO}/devkitARM) +endif() + +if(NOT DEFINED UNITY) + set(UNITY ON) +endif() + +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 -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") +set(CMAKE_LIBRARY_ARCHITECTURE arm-none-eabi CACHE INTERNAL "abi") +set(CMAKE_AR ${cross_prefix}ar CACHE INTERNAL "archiver") +set(CMAKE_C_COMPILER ${cross_prefix}gcc CACHE INTERNAL "c compiler") +set(CMAKE_CXX_COMPILER ${cross_prefix}g++ CACHE INTERNAL "cxx compiler") +set(CMAKE_ASM_COMPILER ${cross_prefix}gcc CACHE INTERNAL "assembler") +if(NOT UNITY) + set(common_flags "${arch_flags} -fno-reorder-functions -fno-optimize-sibling-calls ${inc_flags}") +else() + set(common_flags "${arch_flags} -fno-gcse -fno-tree-vectorize ${inc_flags}") +endif() +set(CMAKE_C_FLAGS ${common_flags} CACHE INTERNAL "c compiler flags") +set(CMAKE_ASM_FLAGS ${common_flags} CACHE INTERNAL "c compiler flags") +set(CMAKE_CXX_FLAGS ${common_flags} CACHE INTERNAL "cxx compiler flags") +set(CMAKE_LINKER ${cross_prefix}ld CACHE INTERNAL "linker") + +set(CMAKE_EXE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "exe link flags") +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(PKG_CONFIG_EXECUTABLE "/dev/null" CACHE INTERNAL "" FORCE) + +set(FIXUP ${toolchain_bin_dir}/psp2-fixup) +set(RAW2C ${DEVKITARM}/bin/raw2c) + +set(PSP2 ON) +add_definitions(-DPSP2) + +set(CMAKE_C_COMPILER_WORKS 1) # Skip test
@@ -0,0 +1,61 @@
+/* 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 "font.h" + +#include <vita2d.h> + +#define CELL_HEIGHT 16 +#define CELL_WIDTH 16 +#define GLYPH_HEIGHT 12 + +struct GUIFont { + vita2d_texture* tex; +}; + +struct GUIFont* GUIFontCreate(void) { + struct GUIFont* guiFont = malloc(sizeof(struct GUIFont)); + if (!guiFont) { + return 0; + } + guiFont->tex = vita2d_load_PNG_buffer(font); + return guiFont; +} + +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; + } + struct GUIFontGlyphMetric metric = defaultFontMetrics[c]; + vita2d_draw_texture_tint_part(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; + } +}
@@ -0,0 +1,89 @@
+/* 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 "util/gui.h" +#include "util/gui/font.h" +#include "util/gui/file-select.h" + +#include <psp2/ctrl.h> +#include <psp2/kernel/processmgr.h> +#include <psp2/moduleinfo.h> + +#include <vita2d.h> + +PSP2_MODULE_INFO(0, 0, "mGBA"); + +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); + + vita2d_init(); + struct GUIFont* font = GUIFontCreate(); + GBAPSP2Setup(); + struct GUIParams params = { + PSP2_HORIZONTAL_PIXELS, PSP2_VERTICAL_PIXELS, + font, _drawStart, _drawEnd, _pollInput + }; + + while (true) { + char path[256]; + if (!selectFile(¶ms, "cache0:", path, sizeof(path), "gba")) { + break; + } + if (!GBAPSP2LoadROM(path)) { + continue; + } + GBAPSP2Runloop(); + GBAPSP2UnloadROM(); + } + + GBAPSP2Teardown(); + + GUIFontDestroy(font); + vita2d_fini(); + + sceKernelExitProcess(0); + return 0; +}
@@ -0,0 +1,53 @@
+/* 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/memory.h" +#include "util/vector.h" + +#include <psp2/kernel/sysmem.h> +#include <psp2/types.h> + +DECLARE_VECTOR(SceUIDList, SceUID); +DEFINE_VECTOR(SceUIDList, SceUID); + +static struct SceUIDList uids; +static bool listInit = false; + +void* anonymousMemoryMap(size_t size) { + if (!listInit) { + SceUIDListInit(&uids, 8); + } + if (size & 0xFFF) { + // Align to 4kB pages + size += ((~size) & 0xFFF) + 1; + } + SceUID memblock = sceKernelAllocMemBlock("anon", SCE_KERNEL_MEMBLOCK_TYPE_USER_RW, size, 0); + if (memblock < 0) { + return 0; + } + *SceUIDListAppend(&uids) = memblock; + void* ptr; + if (sceKernelGetMemBlockBase(memblock, &ptr) < 0) { + return 0; + } + return ptr; +} + +void mappedMemoryFree(void* memory, size_t size) { + UNUSED(size); + size_t i; + for (i = 0; i < SceUIDListSize(&uids); ++i) { + SceUID uid = *SceUIDListGetPointer(&uids, i); + void* ptr; + if (sceKernelGetMemBlockBase(uid, &ptr) < 0) { + continue; + } + if (ptr == memory) { + sceKernelFreeMemBlock(uid); + SceUIDListUnshift(&uids, i, 1); + return; + } + } +}
@@ -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
@@ -0,0 +1,216 @@
+/* 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/context.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 <psp2/audioout.h> +#include <psp2/ctrl.h> +#include <psp2/display.h> +#include <psp2/gxm.h> +#include <psp2/kernel/sysmem.h> + +#include <vita2d.h> + +static struct GBAContext context; +static struct GBAVideoSoftwareRenderer renderer; +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() { + GBAContextInit(&context, 0); + struct GBAOptions opts = { + .useBios = true, + .logLevel = 0, + .idleOptimization = IDLE_LOOP_DETECT + }; + GBAConfigLoadDefaults(&context.config, &opts); + _mapVitaKey(&context.inputMap, PSP2_CTRL_CROSS, GBA_KEY_A); + _mapVitaKey(&context.inputMap, PSP2_CTRL_CIRCLE, GBA_KEY_B); + _mapVitaKey(&context.inputMap, PSP2_CTRL_START, GBA_KEY_START); + _mapVitaKey(&context.inputMap, PSP2_CTRL_SELECT, GBA_KEY_SELECT); + _mapVitaKey(&context.inputMap, PSP2_CTRL_UP, GBA_KEY_UP); + _mapVitaKey(&context.inputMap, PSP2_CTRL_DOWN, GBA_KEY_DOWN); + _mapVitaKey(&context.inputMap, PSP2_CTRL_LEFT, GBA_KEY_LEFT); + _mapVitaKey(&context.inputMap, PSP2_CTRL_RIGHT, GBA_KEY_RIGHT); + _mapVitaKey(&context.inputMap, PSP2_CTRL_LTRIGGER, GBA_KEY_L); + _mapVitaKey(&context.inputMap, PSP2_CTRL_RTRIGGER, GBA_KEY_R); + + struct GBAAxis desc = { GBA_KEY_DOWN, GBA_KEY_UP, 192, 64 }; + GBAInputBindAxis(&context.inputMap, PSP2_INPUT, 0, &desc); + desc = (struct GBAAxis) { GBA_KEY_RIGHT, GBA_KEY_LEFT, 192, 64 }; + GBAInputBindAxis(&context.inputMap, PSP2_INPUT, 1, &desc); + + tex = vita2d_create_empty_texture_format(256, 256, SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR); + + GBAVideoSoftwareRendererCreate(&renderer); + renderer.outputBuffer = vita2d_texture_get_datap(tex); + renderer.outputBufferStride = 256; + GBAVideoAssociateRenderer(&context.gba->video, &renderer.d); + printf("%s starting", projectName); +} + +bool GBAPSP2LoadROM(const char* path) { + if (!GBAContextLoadROM(&context, path, true)) { + printf("%s failed to load!", path); + return false; + } + printf("%s loaded, starting...", path); + GBAContextStart(&context); + char gameTitle[13]; + GBAGetGameTitle(context.gba, gameTitle); + printf("%s started!", gameTitle); + double ratio = GBAAudioCalculateRatio(1, 60, 1); + blip_set_rates(context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio); + blip_set_rates(context.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); + return true; +} + +void GBAPSP2Runloop(void) { + int activeKeys = 0; + + bool fsToggle = false; + while (true) { + 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(&context.inputMap, PSP2_INPUT, pad.buttons, 0); + enum GBAKey angles = GBAInputMapAxis(&context.inputMap, PSP2_INPUT, 0, pad.ly); + if (angles != GBA_KEY_NONE) { + activeKeys |= 1 << angles; + } + angles = GBAInputMapAxis(&context.inputMap, PSP2_INPUT, 1, pad.lx); + if (angles != GBA_KEY_NONE) { + activeKeys |= 1 << angles; + } + angles = GBAInputMapAxis(&context.inputMap, PSP2_INPUT, 2, pad.ry); + if (angles != GBA_KEY_NONE) { + activeKeys |= 1 << angles; + } + angles = GBAInputMapAxis(&context.inputMap, PSP2_INPUT, 3, pad.rx); + if (angles != GBA_KEY_NONE) { + activeKeys |= 1 << angles; + } + + GBAContextFrame(&context, activeKeys); + + MutexLock(&audioContext.mutex); + while (blip_samples_avail(context.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(context.gba->audio.left, &samples[0].left, PSP2_SAMPLES, true); + blip_read_samples(context.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(); + } +} + +void GBAPSP2UnloadROM(void) { + GBAContextStop(&context); +} + +void GBAPSP2Teardown(void) { + GBAContextDeinit(&context); + 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); + } +} + +__attribute__((noreturn, weak)) void __assert_func(const char* file, int line, const char* func, const char* expr) { + printf("ASSERT FAILED: %s in %s at %s:%i\n", expr, func, file, line); + exit(1); +}
@@ -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); + +bool GBAPSP2LoadROM(const char* path); +void GBAPSP2Runloop(void); +void GBAPSP2UnloadROM(void); + +void GBAPSP2Draw(void); + +#endif
@@ -0,0 +1,195 @@
+/* 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 "sce-vfs.h" + +#include <psp2/io/dirent.h> + +#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); +static ssize_t _vfsceWrite(struct VFile* vf, const void* buffer, size_t size); +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)); + if (!vfsce) { + return 0; + } + + vfsce->fd = sceIoOpen(path, flags, mode); + if (vfsce->fd < 0) { + free(vfsce); + return 0; + } + + vfsce->d.close = _vfsceClose; + vfsce->d.seek = _vfsceSeek; + vfsce->d.read = _vfsceRead; + vfsce->d.readline = 0; + vfsce->d.write = _vfsceWrite; + vfsce->d.map = _vfsceMap; + vfsce->d.unmap = _vfsceUnmap; + vfsce->d.truncate = _vfsceTruncate; + vfsce->d.size = _vfsceSize; + vfsce->d.sync = _vfsceSync; + + return &vfsce->d; +} + +bool _vfsceClose(struct VFile* vf) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + + return sceIoClose(vfsce->fd) >= 0; +} + +off_t _vfsceSeek(struct VFile* vf, off_t offset, int whence) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + return sceIoLseek(vfsce->fd, offset, whence); +} + +ssize_t _vfsceRead(struct VFile* vf, void* buffer, size_t size) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + return sceIoRead(vfsce->fd, buffer, size); +} + +ssize_t _vfsceWrite(struct VFile* vf, const void* buffer, size_t size) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + return sceIoWrite(vfsce->fd, buffer, size); +} + +static void* _vfsceMap(struct VFile* vf, size_t size, int flags) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + UNUSED(flags); + void* buffer = anonymousMemoryMap(size); + if (buffer) { + sceIoRead(vfsce->fd, buffer, size); + } + return buffer; +} + +static void _vfsceUnmap(struct VFile* vf, void* memory, size_t size) { + UNUSED(vf); + mappedMemoryFree(memory, size); +} + +static void _vfsceTruncate(struct VFile* vf, size_t size) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + // TODO +} + +ssize_t _vfsceSize(struct VFile* vf) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + SceOff cur = sceIoLseek(vfsce->fd, 0, SEEK_CUR); + SceOff end = sceIoLseek(vfsce->fd, 0, SEEK_END); + 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; +}
@@ -0,0 +1,18 @@
+/* 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_VFS_H +#define SCE_VFS_H + +#ifdef PSP2 +#include <psp2/types.h> +#include <psp2/io/fcntl.h> +#else +#include <pspiofilemgr.h> +#endif + +struct VFile* VFileOpenSce(const char* path, int flags, SceMode mode); + +#endif
@@ -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 <psp2/kernel/threadmgr.h> + +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
@@ -8,7 +8,9 @@
*/ #include <stdio.h> +#ifndef PSP2 #include <ctype.h> +#endif #include <string.h> #include "ini.h"
@@ -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
@@ -6,7 +6,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef COMMON_H #define COMMON_H +#ifndef PSP2 #include <ctype.h> +#endif #include <fcntl.h> #include <inttypes.h> #include <limits.h>
@@ -13,6 +13,8 @@ #ifdef USE_PTHREADS
#include "platform/posix/threading.h" #elif _WIN32 #include "platform/windows/threading.h" +#elif PSP2 +#include "platform/psp2/threading.h" #else #define DISABLE_THREADING #endif
@@ -5,6 +5,10 @@ * 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 "vfs.h" +#ifdef PSP2 +#include "platform/psp2/sce-vfs.h" +#endif + struct VFile* VFileOpen(const char* path, int flags) { #ifdef USE_VFS_FILE const char* chflags;@@ -30,6 +34,30 @@ chflags = "rb";
break; } return VFileFOpen(path, chflags); +#elif defined(PSP2) + int sceFlags = PSP2_O_RDONLY; + switch (flags & O_ACCMODE) { + case O_WRONLY: + sceFlags = PSP2_O_WRONLY; + break; + case O_RDWR: + sceFlags = PSP2_O_RDWR; + break; + case O_RDONLY: + sceFlags = PSP2_O_RDONLY; + break; + } + + if (flags & O_APPEND) { + sceFlags |= PSP2_O_APPEND; + } + if (flags & O_TRUNC) { + sceFlags |= PSP2_O_TRUNC; + } + if (flags & O_CREAT) { + sceFlags |= PSP2_O_CREAT; + } + return VFileOpenSce(path, sceFlags, 0666); #else return VFileOpenFD(path, flags); #endif