mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'port/psp2' into port/crucible
This commit is contained in:
commit
91aee93a4e
|
@ -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)
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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 <vita2d.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
|
@ -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 <psp2/ctrl.h>
|
||||
#include <psp2/display.h>
|
||||
#include <psp2/gxm.h>
|
||||
#include <psp2/moduleinfo.h>
|
||||
#include <psp2/kernel/memorymgr.h>
|
||||
#include <psp2/kernel/processmgr.h>
|
||||
#include <psp2/moduleinfo.h>
|
||||
|
||||
#include <vita2d.h>
|
||||
|
||||
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;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include "util/memory.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
#include <psp2/kernel/memorymgr.h>
|
||||
#include <psp2/kernel/sysmem.h>
|
||||
#include <psp2/types.h>
|
||||
|
||||
DECLARE_VECTOR(SceUIDList, SceUID);
|
||||
|
|
|
@ -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,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 <psp2/audioout.h>
|
||||
#include <psp2/ctrl.h>
|
||||
#include <psp2/display.h>
|
||||
#include <psp2/gxm.h>
|
||||
#include <psp2/kernel/sysmem.h>
|
||||
|
||||
#include <vita2d.h>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -5,16 +5,29 @@
|
|||
* 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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
#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
|
||||
|
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue