mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'port/psp2'
This commit is contained in:
commit
750463d850
|
@ -183,7 +183,7 @@ if(APPLE)
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.6")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.6")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT HAIKU AND NOT MSVC)
|
if(NOT HAIKU AND NOT MSVC AND NOT PSP2)
|
||||||
list(APPEND OS_LIB m)
|
list(APPEND OS_LIB m)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -215,6 +215,10 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm.*")
|
||||||
enable_language(ASM)
|
enable_language(ASM)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(PSP2)
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-format")
|
||||||
|
endif()
|
||||||
|
|
||||||
include(CheckFunctionExists)
|
include(CheckFunctionExists)
|
||||||
check_function_exists(strdup HAVE_STRDUP)
|
check_function_exists(strdup HAVE_STRDUP)
|
||||||
check_function_exists(strndup HAVE_STRNDUP)
|
check_function_exists(strndup HAVE_STRNDUP)
|
||||||
|
@ -453,6 +457,10 @@ if(WII)
|
||||||
add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/wii ${CMAKE_BINARY_DIR})
|
add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/wii ${CMAKE_BINARY_DIR})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(PSP2)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/psp2 ${CMAKE_BINARY_DIR}/psp2)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(BUILD_PERF)
|
if(BUILD_PERF)
|
||||||
set(PERF_SRC ${CMAKE_SOURCE_DIR}/src/platform/test/perf-main.c)
|
set(PERF_SRC ${CMAKE_SOURCE_DIR}/src/platform/test/perf-main.c)
|
||||||
if(UNIX AND NOT APPLE)
|
if(UNIX AND NOT APPLE)
|
||||||
|
|
|
@ -277,8 +277,8 @@ void _rtcUpdateClock(struct GBACartridgeHardware* hw) {
|
||||||
t = time(0);
|
t = time(0);
|
||||||
}
|
}
|
||||||
struct tm date;
|
struct tm date;
|
||||||
#ifdef _WIN32
|
#if defined(_WIN32) || defined(PSP2)
|
||||||
date = *localtime(&t);
|
localtime_s(&date, &t);
|
||||||
#else
|
#else
|
||||||
localtime_r(&t, &date);
|
localtime_r(&t, &date);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -18,6 +18,10 @@
|
||||||
#include <strsafe.h>
|
#include <strsafe.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef PSP2
|
||||||
|
#include <psp2/io/stat.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define SECTION_NAME_MAX 128
|
#define SECTION_NAME_MAX 128
|
||||||
|
|
||||||
static const char* _lookupValue(const struct GBAConfig* config, const char* key) {
|
static const char* _lookupValue(const struct GBAConfig* config, const char* key) {
|
||||||
|
@ -135,13 +139,8 @@ bool GBAConfigSavePath(const struct GBAConfig* config, const char* path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAConfigMakePortable(const struct GBAConfig* config) {
|
void GBAConfigMakePortable(const struct GBAConfig* config) {
|
||||||
struct VFile* portable;
|
struct VFile* portable = 0;
|
||||||
#ifndef _WIN32
|
#ifdef _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
|
|
||||||
char out[MAX_PATH];
|
char out[MAX_PATH];
|
||||||
wchar_t wpath[MAX_PATH];
|
wchar_t wpath[MAX_PATH];
|
||||||
wchar_t wprojectName[MAX_PATH];
|
wchar_t wprojectName[MAX_PATH];
|
||||||
|
@ -152,6 +151,13 @@ void GBAConfigMakePortable(const struct GBAConfig* config) {
|
||||||
WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, MAX_PATH, 0, 0);
|
WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, MAX_PATH, 0, 0);
|
||||||
StringCchCatA(out, MAX_PATH, "\\portable.ini");
|
StringCchCatA(out, MAX_PATH, "\\portable.ini");
|
||||||
portable = VFileOpen(out, O_WRONLY | O_CREAT);
|
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
|
#endif
|
||||||
if (portable) {
|
if (portable) {
|
||||||
portable->close(portable);
|
portable->close(portable);
|
||||||
|
@ -161,22 +167,7 @@ void GBAConfigMakePortable(const struct GBAConfig* config) {
|
||||||
|
|
||||||
void GBAConfigDirectory(char* out, size_t outLength) {
|
void GBAConfigDirectory(char* out, size_t outLength) {
|
||||||
struct VFile* portable;
|
struct VFile* portable;
|
||||||
#ifndef _WIN32
|
#ifdef _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
|
|
||||||
wchar_t wpath[MAX_PATH];
|
wchar_t wpath[MAX_PATH];
|
||||||
wchar_t wprojectName[MAX_PATH];
|
wchar_t wprojectName[MAX_PATH];
|
||||||
MultiByteToWideChar(CP_UTF8, 0, projectName, -1, wprojectName, MAX_PATH);
|
MultiByteToWideChar(CP_UTF8, 0, projectName, -1, wprojectName, MAX_PATH);
|
||||||
|
@ -196,6 +187,25 @@ void GBAConfigDirectory(char* out, size_t outLength) {
|
||||||
CreateDirectoryW(wpath, NULL);
|
CreateDirectoryW(wpath, NULL);
|
||||||
}
|
}
|
||||||
WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, outLength, 0, 0);
|
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
|
#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 @@ http://code.google.com/p/inih/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#ifndef PSP2
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#endif
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "ini.h"
|
#include "ini.h"
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
# 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
|
# 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/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
#ifdef __ARM_NEON
|
#if defined(__ARM_NEON) && !defined(PSP2)
|
||||||
# r0: Destination
|
# r0: Destination
|
||||||
# r1: Source
|
# r1: Source
|
||||||
# r2: Number of words to copy as halfwords
|
# r2: Number of words to copy as halfwords
|
||||||
|
|
|
@ -6,7 +6,9 @@
|
||||||
#ifndef COMMON_H
|
#ifndef COMMON_H
|
||||||
#define COMMON_H
|
#define COMMON_H
|
||||||
|
|
||||||
|
#ifndef PSP2
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#endif
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
#include "platform/posix/threading.h"
|
#include "platform/posix/threading.h"
|
||||||
#elif _WIN32
|
#elif _WIN32
|
||||||
#include "platform/windows/threading.h"
|
#include "platform/windows/threading.h"
|
||||||
|
#elif PSP2
|
||||||
|
#include "platform/psp2/threading.h"
|
||||||
#else
|
#else
|
||||||
#define DISABLE_THREADING
|
#define DISABLE_THREADING
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -5,6 +5,10 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include "vfs.h"
|
#include "vfs.h"
|
||||||
|
|
||||||
|
#ifdef PSP2
|
||||||
|
#include "platform/psp2/sce-vfs.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
struct VFile* VFileOpen(const char* path, int flags) {
|
struct VFile* VFileOpen(const char* path, int flags) {
|
||||||
#ifdef USE_VFS_FILE
|
#ifdef USE_VFS_FILE
|
||||||
const char* chflags;
|
const char* chflags;
|
||||||
|
@ -30,6 +34,30 @@ struct VFile* VFileOpen(const char* path, int flags) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return VFileFOpen(path, chflags);
|
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
|
#else
|
||||||
return VFileOpenFD(path, flags);
|
return VFileOpenFD(path, flags);
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue