diff --git a/.github/workflows/build-vita.yml b/.github/workflows/build-vita.yml
new file mode 100644
index 00000000..30edd025
--- /dev/null
+++ b/.github/workflows/build-vita.yml
@@ -0,0 +1,80 @@
+name: re3 makefile VitaSDK (PlayStation Vita)
+
+on:
+ workflow_dispatch:
+ pull_request:
+ push:
+ branches: [ master ]
+ release:
+ types: [ published ]
+
+jobs:
+ build-ps-vita:
+ runs-on: ubuntu-latest
+ container: vitasdk/vitasdk:latest
+
+ steps:
+ - name: Install CI prerequisites
+ run: apk add --no-cache build-base git make cmake bash wget tar bzip2 patch
+
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Build vitaShaRK
+ run: |
+ git clone --depth 1 https://github.com/Rinnegatamante/vitaShaRK.git
+ make -C vitaShaRK -j"$(nproc)" install
+
+ - name: Build math-neon
+ run: |
+ git clone --depth 1 https://github.com/Rinnegatamante/math-neon.git
+ make -C math-neon -j"$(nproc)" install
+
+ - name: Build vitaGL
+ run: |
+ git clone --depth 1 https://github.com/Rinnegatamante/vitaGL.git
+ make -C vitaGL NO_DEBUG=1 PHYCONT_ON_DEMAND=1 ENABLE_LEGACY_PIPELINE=1 -j"$(nproc)" install
+
+ - name: Build librw-vita (fork, kept building against current vitaGL)
+ run: |
+ git clone --depth 1 https://github.com/Dima353/librw-vita.git
+ make -C librw-vita -j"$(nproc)" install
+
+ - name: Build OpenAL-Soft (Vita fork)
+ run: |
+ git clone --depth 1 -b vita-1.19.1 https://github.com/Dima353/openal-soft.git
+ mkdir -p openal-soft/build && cd openal-soft/build
+ cmake -DCMAKE_TOOLCHAIN_FILE=${VITASDK}/share/vita.toolchain.cmake -DCMAKE_BUILD_TYPE=Release \
+ -DCMAKE_C_FLAGS="-D_GNU_SOURCE -Wno-error=implicit-function-declaration -Wno-error=int-conversion" ..
+ make -j"$(nproc)" install
+
+ - name: Build mpg123
+ run: |
+ wget -q https://www.mpg123.de/download/mpg123-1.33.5.tar.bz2
+ tar -xjf mpg123-1.33.5.tar.bz2
+ wget -q -O mpg123-1.33.5/mpg123.patch https://raw.githubusercontent.com/vitasdk/packages/master/mpg123/mpg123.patch
+ cd mpg123-1.33.5
+ patch -p1 -i mpg123.patch
+ mkdir _build && cd _build
+ cmake ../ports/cmake \
+ -DCMAKE_TOOLCHAIN_FILE=${VITASDK}/share/vita.toolchain.cmake -DCMAKE_BUILD_TYPE=Release \
+ -DCMAKE_C_FLAGS='-DDONT_CATCH_SIGNALS' -DACCURATE_ROUNDING=OFF -DHAVE_IPV6=0 -DHAVE_NETWORK=0
+ make -j"$(nproc)" install
+
+ - name: Build SDL2 (VGL backend)
+ run: |
+ git clone --depth 1 https://github.com/Northfear/SDL.git
+ cd SDL
+ cmake -S. -Bbuild -DCMAKE_TOOLCHAIN_FILE=${VITASDK}/share/vita.toolchain.cmake -DCMAKE_BUILD_TYPE=Release -DVIDEO_VITA_VGL=ON -DCMAKE_POLICY_VERSION_MINIMUM=3.5
+ cmake --build build -- -j"$(nproc)"
+ cmake --install build
+
+ - name: Build re3 (GTA3.vpk)
+ run: make -j"$(nproc)"
+
+ - name: Upload VPK artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: vita-gta3-vpk
+ path: GTA3.vpk
+ if-no-files-found: error
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..ae44ba7b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,70 @@
+TITLE := "Grand Theft Auto III"
+TITLE_ID := GTA3RT000
+TARGET := GTA3
+
+SOURCES := src src/animation src/audio src/audio/oal src/audio/eax src/buildings src/control src/collision src/core src/entities src/math src/modelinfo src/objects src/peds src/renderer src/rw src/save src/skel src/skel/glfw src/text src/vehicles src/weapons src/extras src/fakerw
+INCLUDES := src src/animation src/audio src/audio/oal src/audio/eax src/buildings src/control src/collision src/core src/entities src/math src/modelinfo src/objects src/peds src/renderer src/rw src/save src/skel src/skel/glfw src/text src/vehicles src/weapons src/extras src/fakerw librw
+
+CFILES := $(foreach dir,$(SOURCES), $(wildcard $(dir)/*.c))
+CPPFILES := $(foreach dir,$(SOURCES), $(wildcard $(dir)/*.cpp))
+BINFILES := $(foreach dir,$(DATA), $(wildcard $(dir)/*.bin))
+OBJS := $(addsuffix .o,$(BINFILES)) $(CFILES:.c=.o) $(CPPFILES:.cpp=.o)
+
+INCLUDE := $(foreach dir,$(INCLUDES),-I$(dir))
+
+PREFIX = arm-vita-eabi
+CC = $(PREFIX)-gcc
+CXX = $(PREFIX)-g++
+ARCH := -mtune=cortex-a9 -march=armv7-a -mfpu=neon
+CFLAGS := -g -Wl,-q,--no-enum-size-warning -fno-short-enums -fno-optimize-sibling-calls -O2 -ftree-vectorize -mfloat-abi=hard -fno-builtin-memcpy $(ARCH) $(DEFINES)
+CFLAGS += $(INCLUDE) -DPSP2 -DNDEBUG -DMASTER -DLIBRW -DRW_GL3 -DAUDIO_OAL -DLIBRW_GLAD
+CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
+ASFLAGS := -g $(ARCH)
+LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $*.map)
+LIBS := -lrw -lopenal -lSDL2 -lvita2d -lvitaGL -lSceAppMgr_stub -lSceDisplay_stub -lSceCommonDialog_stub -lSceLibKernel_stub \
+ -lSceSysmodule_stub -lvitashark -lSceShaccCg_stub -lSceShaccCgExt -lvitaGL -lmathneon -lSceGxm_stub -lScePower_stub \
+ -lSceCtrl_stub -lSceHid_stub -lSceAudio_stub -lSceAudioIn_stub -lSceTouch_stub -lm -lpthread -lmpg123 -lSceMotion_stub \
+ -lSceSysmodule_stub -lSceAvPlayer_stub -lSceKernelDmacMgr_stub -ltaihen_stub
+
+all: $(TARGET).vpk
+
+%.vpk: eboot.bin
+ vita-mksfoex -s APP_VER=01.50 -s TITLE_ID=$(TITLE_ID) -d ATTRIBUTE2=12 $(TITLE) param.sfo
+ vita-pack-vpk -s param.sfo -b eboot.bin \
+ --add sce_sys/icon0.png=sce_sys/icon0.png \
+ --add sce_sys/livearea/contents/bg.png=sce_sys/livearea/contents/bg.png \
+ --add sce_sys/livearea/contents/startup.png=sce_sys/livearea/contents/startup.png \
+ --add sce_sys/livearea/contents/template.xml=sce_sys/livearea/contents/template.xml \
+ --add sce_sys/manual/001.png=sce_sys/manual/001.png \
+ --add sce_sys/manual/002.png=sce_sys/manual/002.png \
+ --add sce_sys/manual/003.png=sce_sys/manual/003.png \
+ --add sce_sys/manual/004.png=sce_sys/manual/004.png \
+ --add sce_sys/manual/005.png=sce_sys/manual/005.png \
+ --add sce_sys/manual/006.png=sce_sys/manual/006.png \
+ --add sce_sys/manual/007.png=sce_sys/manual/007.png \
+ --add sce_sys/manual/008.png=sce_sys/manual/008.png \
+ --add sce_sys/manual/009.png=sce_sys/manual/009.png \
+ --add sce_sys/manual/010.png=sce_sys/manual/010.png \
+ --add sce_sys/manual/011.png=sce_sys/manual/011.png \
+ --add sce_sys/manual/012.png=sce_sys/manual/012.png \
+ --add sce_sys/manual/013.png=sce_sys/manual/013.png \
+ --add sce_sys/manual/014.png=sce_sys/manual/014.png \
+ --add sce_sys/manual/015.png=sce_sys/manual/015.png \
+ --add sce_sys/manual/016.png=sce_sys/manual/016.png \
+ --add sce_sys/manual/017.png=sce_sys/manual/017.png \
+ --add sce_sys/manual/018.png=sce_sys/manual/018.png \
+ --add sce_sys/manual/019.png=sce_sys/manual/019.png \
+ --add sce_sys/manual/020.png=sce_sys/manual/020.png \
+ $(TARGET).vpk
+
+eboot.bin: $(TARGET).velf
+ vita-make-fself -c -s $< $@
+
+%.velf: %.elf
+ vita-elf-create $< $@
+
+$(TARGET).elf: $(OBJS)
+ $(CXX) $(CXXFLAGS) $^ $(LIBS) -o $@
+
+clean:
+ @rm -rf $(TARGET).vpk $(TARGET).velf $(TARGET).elf $(OBJS) eboot.bin param.sfo
diff --git a/sce_sys/icon0.png b/sce_sys/icon0.png
new file mode 100644
index 00000000..ad8155c5
Binary files /dev/null and b/sce_sys/icon0.png differ
diff --git a/sce_sys/livearea/contents/bg.png b/sce_sys/livearea/contents/bg.png
new file mode 100644
index 00000000..d4cf30af
Binary files /dev/null and b/sce_sys/livearea/contents/bg.png differ
diff --git a/sce_sys/livearea/contents/startup.png b/sce_sys/livearea/contents/startup.png
new file mode 100644
index 00000000..ceb00b83
Binary files /dev/null and b/sce_sys/livearea/contents/startup.png differ
diff --git a/sce_sys/livearea/contents/template.xml b/sce_sys/livearea/contents/template.xml
new file mode 100644
index 00000000..60e0b8ea
--- /dev/null
+++ b/sce_sys/livearea/contents/template.xml
@@ -0,0 +1,11 @@
+
+
+
+
+ bg.png
+
+
+
+ startup.png
+
+
\ No newline at end of file
diff --git a/sce_sys/manual/001.png b/sce_sys/manual/001.png
new file mode 100644
index 00000000..640eea43
Binary files /dev/null and b/sce_sys/manual/001.png differ
diff --git a/sce_sys/manual/002.png b/sce_sys/manual/002.png
new file mode 100644
index 00000000..f4cc6594
Binary files /dev/null and b/sce_sys/manual/002.png differ
diff --git a/sce_sys/manual/003.png b/sce_sys/manual/003.png
new file mode 100644
index 00000000..07972933
Binary files /dev/null and b/sce_sys/manual/003.png differ
diff --git a/sce_sys/manual/004.png b/sce_sys/manual/004.png
new file mode 100644
index 00000000..5af09ed6
Binary files /dev/null and b/sce_sys/manual/004.png differ
diff --git a/sce_sys/manual/005.png b/sce_sys/manual/005.png
new file mode 100644
index 00000000..76c7f261
Binary files /dev/null and b/sce_sys/manual/005.png differ
diff --git a/sce_sys/manual/006.png b/sce_sys/manual/006.png
new file mode 100644
index 00000000..19827d81
Binary files /dev/null and b/sce_sys/manual/006.png differ
diff --git a/sce_sys/manual/007.png b/sce_sys/manual/007.png
new file mode 100644
index 00000000..39261a77
Binary files /dev/null and b/sce_sys/manual/007.png differ
diff --git a/sce_sys/manual/008.png b/sce_sys/manual/008.png
new file mode 100644
index 00000000..ee260688
Binary files /dev/null and b/sce_sys/manual/008.png differ
diff --git a/sce_sys/manual/009.png b/sce_sys/manual/009.png
new file mode 100644
index 00000000..c33e8b61
Binary files /dev/null and b/sce_sys/manual/009.png differ
diff --git a/sce_sys/manual/010.png b/sce_sys/manual/010.png
new file mode 100644
index 00000000..8ccf25ab
Binary files /dev/null and b/sce_sys/manual/010.png differ
diff --git a/sce_sys/manual/011.png b/sce_sys/manual/011.png
new file mode 100644
index 00000000..38b78355
Binary files /dev/null and b/sce_sys/manual/011.png differ
diff --git a/sce_sys/manual/012.png b/sce_sys/manual/012.png
new file mode 100644
index 00000000..b587949e
Binary files /dev/null and b/sce_sys/manual/012.png differ
diff --git a/sce_sys/manual/013.png b/sce_sys/manual/013.png
new file mode 100644
index 00000000..074dc65f
Binary files /dev/null and b/sce_sys/manual/013.png differ
diff --git a/sce_sys/manual/014.png b/sce_sys/manual/014.png
new file mode 100644
index 00000000..44b60c50
Binary files /dev/null and b/sce_sys/manual/014.png differ
diff --git a/sce_sys/manual/015.png b/sce_sys/manual/015.png
new file mode 100644
index 00000000..0472f488
Binary files /dev/null and b/sce_sys/manual/015.png differ
diff --git a/sce_sys/manual/016.png b/sce_sys/manual/016.png
new file mode 100644
index 00000000..7cf9296a
Binary files /dev/null and b/sce_sys/manual/016.png differ
diff --git a/sce_sys/manual/017.png b/sce_sys/manual/017.png
new file mode 100644
index 00000000..3492d008
Binary files /dev/null and b/sce_sys/manual/017.png differ
diff --git a/sce_sys/manual/018.png b/sce_sys/manual/018.png
new file mode 100644
index 00000000..2738ae0e
Binary files /dev/null and b/sce_sys/manual/018.png differ
diff --git a/sce_sys/manual/019.png b/sce_sys/manual/019.png
new file mode 100644
index 00000000..59952852
Binary files /dev/null and b/sce_sys/manual/019.png differ
diff --git a/sce_sys/manual/020.png b/sce_sys/manual/020.png
new file mode 100644
index 00000000..205b33fb
Binary files /dev/null and b/sce_sys/manual/020.png differ
diff --git a/src/audio/oal/oal_utils.cpp b/src/audio/oal/oal_utils.cpp
index e4cb0b77..fa7b8371 100644
--- a/src/audio/oal/oal_utils.cpp
+++ b/src/audio/oal/oal_utils.cpp
@@ -51,6 +51,7 @@ using namespace re3_openal;
void EFXInit()
{
+#ifndef PSP2
/* Define a macro to help load the function pointers. */
#define LOAD_PROC(T, x) ((x) = (T)alGetProcAddress(#x))
LOAD_PROC(LPALGENEFFECTS, alGenEffects);
@@ -89,6 +90,7 @@ void EFXInit()
LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTF, alGetAuxiliaryEffectSlotf);
LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTFV, alGetAuxiliaryEffectSlotfv);
#undef LOAD_PROC
+#endif
}
void SetEffectsLevel(ALuint uiFilter, float level)
diff --git a/src/audio/sampman_oal.cpp b/src/audio/sampman_oal.cpp
index d59b86e6..a145ee99 100644
--- a/src/audio/sampman_oal.cpp
+++ b/src/audio/sampman_oal.cpp
@@ -18,7 +18,7 @@
#include
#include
#include
-#else
+#elif !defined(PSP2)
#define _getcwd getcwd
#endif
@@ -255,7 +255,8 @@ set_new_provider(int index)
usingEAX3 = 0;
_usingEFX = false;
- if ( !strcmp(&providers[index].name[strlen(providers[index].name) - strlen(" EAX3")], " EAX3")
+#if !defined(PSP2)
+ if ( !strcmp(&providers[index].name[strlen(providers[index].name) - strlen(" EAX3")], " EAX3")
&& alcIsExtensionPresent(ALDevice, (ALCchar*)ALC_EXT_EFX_NAME) )
{
@@ -282,7 +283,8 @@ set_new_provider(int index)
alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, ALEffect);
EAX_SetAll(&EAX30_ORIGINAL_PRESETS[EAX_ENVIRONMENT_CAVE]);
}
-
+#endif
+
//SampleManager.SetSpeakerConfig(speaker_type);
if ( IsFXSupported() )
@@ -420,7 +422,7 @@ _ResolveLink(char const *path, char *out)
#else
struct stat sb;
- if (lstat(path, &sb) == -1) {
+ if (_lstat(path, &sb) == -1) {
perror("lstat: ");
return FALSE;
}
@@ -457,7 +459,8 @@ _FindMP3s(void)
WIN32_FIND_DATA fd;
char filepath[MAX_PATH + sizeof(fd.cFileName)];
- if (getcwd(_mp3DirectoryPath, MAX_PATH) == NULL) {
+ // _getcwd on PSP2 returns the data directory; elsewhere it is the real one.
+ if (_getcwd(_mp3DirectoryPath, MAX_PATH) == NULL) {
perror("getcwd: ");
return;
}
@@ -509,6 +512,8 @@ _FindMP3s(void)
{
if (filepathlen > 0)
{
+ // No symlinks on this filesystem, and readlink is a stub here.
+#ifndef PSP2
if (_ResolveLink(filepath, filepath))
{
OutputDebugString("Resolving Link");
@@ -516,6 +521,7 @@ _FindMP3s(void)
bShortcut = TRUE;
}
else
+#endif
{
bShortcut = FALSE;
if (filepathlen > MAX_PATH) {
@@ -568,6 +574,7 @@ _FindMP3s(void)
{
if ( filepathlen > 0 )
{
+#ifndef PSP2
if ( _ResolveLink(filepath, filepath) )
{
OutputDebugString("Resolving Link");
@@ -575,6 +582,7 @@ _FindMP3s(void)
bShortcut = TRUE;
}
else
+#endif
bShortcut = FALSE;
if (aStream[0] && aStream[0]->Open(filepath))
@@ -839,12 +847,14 @@ cSampleManager::Initialise(void)
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
+#ifndef PSP2
if ( alcIsExtensionPresent(ALDevice, (ALCchar*)ALC_EXT_EFX_NAME) )
{
_effectsSupported = providers[index].bSupportsFx;
alGenAuxiliaryEffectSlots(1, &ALEffectSlot);
alGenEffects(1, &ALEffect);
}
+#endif
alGenSources(MAX_STREAMS*2, ALStreamSources[0]);
for ( int32 i = 0; i < MAX_STREAMS; i++ )
diff --git a/src/core/Cam.cpp b/src/core/Cam.cpp
index d4be8585..18478ede 100644
--- a/src/core/Cam.cpp
+++ b/src/core/Cam.cpp
@@ -30,7 +30,11 @@ bool PrintDebugCode = false;
int16 DebugCamMode;
#ifdef FREE_CAM
+#ifdef GTA_HANDHELD
+bool CCamera::bFreeCam = true; // handhelds default the free camera on
+#else
bool CCamera::bFreeCam = false;
+#endif
int nPreviousMode = -1;
#endif
diff --git a/src/core/CdStreamPosix.cpp b/src/core/CdStreamPosix.cpp
index bc9129eb..69f996b1 100644
--- a/src/core/CdStreamPosix.cpp
+++ b/src/core/CdStreamPosix.cpp
@@ -7,7 +7,9 @@
#include
#include
#include
+#ifndef PSP2
#include
+#endif
#include
#include
#include
@@ -26,6 +28,12 @@
#define CDDEBUG(f, ...) debug ("%s: " f "\n", "cdvd_stream", ## __VA_ARGS__)
#define CDTRACE(f, ...) printf("%s: " f "\n", "cdvd_stream", ## __VA_ARGS__)
+#ifdef PSP2
+// Named semaphores do not exist here, and newlib does not declare SEM_FAILED.
+#define USE_UNNAMED_SEM
+#define SEM_FAILED ((sem_t *) 0)
+#endif
+
#ifdef FLUSHABLE_STREAMING
bool flushStream[MAX_CDCHANNELS];
#endif
@@ -198,6 +206,7 @@ CdStreamInitThread(void)
void
CdStreamInit(int32 numChannels)
{
+#ifndef PSP2
struct statvfs fsInfo;
if((statvfs("models/gta3.img", &fsInfo)) < 0)
@@ -206,6 +215,7 @@ CdStreamInit(int32 numChannels)
ASSERT(0);
return;
}
+#endif
#ifdef __linux__
_gdwCdStreamFlags = O_RDONLY | O_NOATIME;
#else
@@ -219,7 +229,11 @@ CdStreamInit(int32 numChannels)
debug("Using no buffered loading for streaming\n");
}
*/
+#ifndef PSP2
void *pBuffer = (void *)RwMallocAlign(CDSTREAM_SECTOR_SIZE, (RwUInt32)fsInfo.f_bsize);
+#else
+ void *pBuffer = (void *)RwMallocAlign(CDSTREAM_SECTOR_SIZE, CDSTREAM_SECTOR_SIZE);
+#endif
ASSERT( pBuffer != nil );
gNumImages = 0;
@@ -245,13 +259,14 @@ GetGTA3ImgSize(void)
struct stat statbuf;
char path[PATH_MAX];
- realpath(gImgNames[0], path);
+ // _realpath is the Vita shim, plain realpath everywhere else - see crossplatform.h
+ _realpath(gImgNames[0], path);
if (stat(path, &statbuf) == -1) {
// Try case-insensitivity
char* real = casepath(gImgNames[0], false);
if (real)
{
- realpath(real, path);
+ _realpath(real, path);
free(real);
if (stat(path, &statbuf) != -1)
goto ok;
diff --git a/src/core/FileMgr.cpp b/src/core/FileMgr.cpp
index 32aa4041..2012fd99 100644
--- a/src/core/FileMgr.cpp
+++ b/src/core/FileMgr.cpp
@@ -32,14 +32,16 @@ static myFILE myfiles[NUMFILES];
#include
#include
#include
+#if !defined(PSP2)
#define _getcwd getcwd
+#endif
// Case-insensitivity on linux (from https://github.com/OneSadCookie/fcaseopen)
void mychdir(char const *path)
{
char* r = casepath(path, false);
if (r) {
- chdir(r);
+ _chdir(r);
free(r);
} else {
errno = ENOENT;
diff --git a/src/core/Frontend.cpp b/src/core/Frontend.cpp
index 1a211429..c16054f8 100644
--- a/src/core/Frontend.cpp
+++ b/src/core/Frontend.cpp
@@ -35,6 +35,10 @@
#include "FileLoader.h"
#include "frontendoption.h"
+#ifdef PSP2
+extern bool useRearpad; // touch input: front panel or rear, see crossplatform.cpp
+#endif
+
// Game has colors inlined in code.
// For easier modification we collect them here:
const CRGBA LABEL_COLOR(235, 170, 50, 255);
@@ -145,11 +149,7 @@ int8 CMenuManager::m_PrefsIslandLoading = ISLAND_LOADING_LOW;
#endif
#ifdef GAMEPAD_MENU
-#ifdef __SWITCH__
-int8 CMenuManager::m_PrefsControllerType = CONTROLLER_NINTENDO_SWITCH;
-#else
-int8 CMenuManager::m_PrefsControllerType = CONTROLLER_XBOXONE;
-#endif
+int8 CMenuManager::m_PrefsControllerType = DEFAULT_CONTROLLER_TYPE;
#endif
int32 CMenuManager::OS_Language = LANG_ENGLISH;
@@ -160,7 +160,11 @@ int8 CMenuManager::m_PrefsVsyncDisp = 1;
int8 CMenuManager::m_PrefsFrameLimiter = 1;
int8 CMenuManager::m_PrefsShowSubtitles = 1;
int8 CMenuManager::m_PrefsSpeakers;
+#ifdef GTA_HANDHELD
+int32 CMenuManager::m_ControlMethod = CONTROL_CLASSIC; // handhelds default to classic controls
+#else
int32 CMenuManager::m_ControlMethod;
+#endif
int8 CMenuManager::m_PrefsDMA = 1;
int32 CMenuManager::m_PrefsLanguage;
uint8 CMenuManager::m_PrefsStereoMono; // unused except restore settings
@@ -1656,6 +1660,13 @@ CMenuManager::Draw()
#endif
}
+#ifdef PSP2
+ if (!strncmp(aScreens[m_nCurrScreen].m_aEntries[i].m_EntryName, "PSP2", 4)) {
+ leftText = TheText.Get("PSP2");
+ rightText = TheText.Get(useRearpad ? "FEM_ON" : "FEM_OFF");
+ }
+#endif
+
float nextItemY = headerHeight + nextYToUse;
float bitAboveNextItemY = nextItemY - 2.0f;
int nextYToCheck = bitAboveNextItemY;
@@ -3685,7 +3696,7 @@ const char* controllerTypesPaths[] = {
nil,
"MODELS/FRONTEND_DS3.TXD",
"MODELS/FRONTEND_DS4.TXD",
-#ifndef RT
+#ifndef RT_CONTROLLERS
"MODELS/FRONTEND_X360.TXD",
#endif
"MODELS/FRONTEND_XONE.TXD",
@@ -3695,12 +3706,14 @@ const char* controllerTypesPaths[] = {
void
CMenuManager::LoadController(int8 type)
{
- // A controller type saved by the other build variant can be out of range here:
- // the RT and vanilla controller enums differ in size (vanilla keeps CONTROLLER_XBOX360),
- // so e.g. Nintendo Switch is index 5 in vanilla but 4 under RT. An out-of-range value
- // would index controllerTypesPaths/controllerTypes out of bounds -> crash. Clamp it.
- if (type < 0 || type >= (int8)ARRAY_SIZE(controllerTypesPaths)) {
- type = CONTROLLER_NINTENDO_SWITCH;
+ // A controller type saved by another build variant can be out of range here, in two
+ // ways: the RT and vanilla enums differ in size (vanilla keeps CONTROLLER_XBOX360),
+ // so e.g. Nintendo Switch is index 5 in vanilla but 4 under RT; and the handhelds
+ // offer fewer types than the .ini may hold. Out of range means controllerTypes read
+ // out of bounds, a pad the menu can no longer select away from, and with it the wrong
+ // button labels and the wrong menu confirm/cancel layout. Clamp it.
+ if (type < 0 || type >= (int8)NUM_MENU_CONTROLLER_TYPES) {
+ type = DEFAULT_CONTROLLER_TYPE;
m_PrefsControllerType = type;
}
@@ -3708,12 +3721,12 @@ CMenuManager::LoadController(int8 type)
{
case CONTROLLER_DUALSHOCK3:
case CONTROLLER_DUALSHOCK4:
-#ifndef RT
+#ifndef RT_CONTROLLERS
case CONTROLLER_DUALSHOCK2: // real DualShock2 uses PlayStation buttons
#endif
CFont::LoadButtons("MODELS/PS3BTNS.TXD");
break;
-#ifdef RT
+#ifdef RT_CONTROLLERS
case CONTROLLER_DUALSHOCK2: // RT: the DS2 slot is a Switch Pro Controller
#endif
case CONTROLLER_NINTENDO_SWITCH:
@@ -5861,7 +5874,334 @@ CMenuManager::WaitForUserCD()
void
CMenuManager::PrintController(void)
{
-#ifdef RT
+#ifdef PSP2
+ // PSV: the Vita port draws labels against its own pad artwork with its own
+ // per-type nudges and justification, so it gets re3-vita's PrintController
+ // verbatim. The RT_CONTROLLERS (Switch) and vanilla bodies below are untouched.
+ const float scale = 0.9f;
+ const float CONTROLLER_SIZE_X = 384.0f;
+ const float CONTROLLER_SIZE_Y = 384.0f;
+ const float CONTROLLER_POS_X = (DEFAULT_SCREEN_WIDTH - CONTROLLER_SIZE_X) / 2.0f;
+ const float CONTROLLER_POS_Y = 140.0f;
+
+ float centerX = CONTROLLER_POS_X + CONTROLLER_SIZE_X / 2;
+ float centerY = CONTROLLER_POS_Y + CONTROLLER_SIZE_Y / 2;
+
+#define X(f) ((f)*scale + centerX)
+#define Y(f) ((f)*scale + centerY)
+
+ m_aFrontEndSprites[FE_CONTROLLERSH].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X((CONTROLLER_SIZE_X + 4.8f) * scale), MENU_Y((CONTROLLER_SIZE_Y + 4.8f) * scale), CRGBA(0, 0, 0, 255));
+ m_aFrontEndSprites[FE_CONTROLLER].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X(CONTROLLER_SIZE_X * scale), MENU_Y(CONTROLLER_SIZE_Y * scale), CRGBA(255, 255, 255, 255));
+ if (m_DisplayControllerOnFoot) {
+ if (CTimer::GetTimeInMillisecondsPauseMode() & 0x400)
+ m_aFrontEndSprites[FE_ARROWS1].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X(CONTROLLER_SIZE_X * scale), MENU_Y(CONTROLLER_SIZE_Y * scale), CRGBA(255, 255, 255, 255));
+ else
+ m_aFrontEndSprites[FE_ARROWS3].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X(CONTROLLER_SIZE_X * scale), MENU_Y(CONTROLLER_SIZE_Y * scale), CRGBA(255, 255, 255, 255));
+ } else {
+ if (CTimer::GetTimeInMillisecondsPauseMode() & 0x400)
+ m_aFrontEndSprites[FE_ARROWS2].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X(CONTROLLER_SIZE_X * scale), MENU_Y(CONTROLLER_SIZE_Y * scale), CRGBA(255, 255, 255, 255));
+ else
+ m_aFrontEndSprites[FE_ARROWS4].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X(CONTROLLER_SIZE_X * scale), MENU_Y(CONTROLLER_SIZE_Y * scale), CRGBA(255, 255, 255, 255));
+ }
+
+ CFont::SetFontStyle(FONT_LOCALE(FONT_BANK));
+ CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * scale), MENU_Y(SMALLESTTEXT_Y_SCALE * scale)); // X
+
+ // CFont::SetColor(CRGBA(128, 128, 128, FadeIn(255)));
+ CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(255)));
+ CFont::SetDropShadowPosition(1);
+ CFont::SetColor(CRGBA(255, 255, 255, FadeIn(255)));
+ CFont::SetWrapx(SCREEN_WIDTH);
+
+ float TEXT_L2_X = 106.0f + CONTROLLER_POS_X - centerX, TEXT_L2_Y = -7.0f + CONTROLLER_POS_Y - centerY;
+ float TEXT_L1_X = 2.0f + CONTROLLER_POS_X - centerX, TEXT_L1_Y = 25.0f + CONTROLLER_POS_Y - centerY, TEXT_L1_Y_VEH = 3.0f + TEXT_L1_Y;
+ float TEXT_DPAD_X = 2.0f + CONTROLLER_POS_X - centerX, TEXT_DPAD_Y = 65.0f + CONTROLLER_POS_Y - centerY;
+ float TEXT_LSTICK_X = 2.0f + CONTROLLER_POS_X - centerX, TEXT_LSTICK_Y = 101.0f + CONTROLLER_POS_Y - centerY;
+ float TEXT_SELECT_X = 382.0f + CONTROLLER_POS_X - centerX, TEXT_SELECT_Y = 141.0f + CONTROLLER_POS_Y - centerY;
+ float TEXT_START_X = 382.0f + CONTROLLER_POS_X - centerX, TEXT_START_Y = 124.0f + CONTROLLER_POS_Y - centerY;
+ float TEXT_R2_X = 274.0F + CONTROLLER_POS_X - centerX, TEXT_R2_Y = -7.0f + CONTROLLER_POS_Y - centerY;
+ float TEXT_R1_X = 382.0f + CONTROLLER_POS_X - centerX, TEXT_R1_Y = 25.0f + CONTROLLER_POS_Y - centerY;
+
+ float TEXT_SQUARE_X = 382.0f + CONTROLLER_POS_X - centerX, TEXT_SQUARE_Y = 39.0f + CONTROLLER_POS_Y - centerY;
+ float TEXT_TRIANGLE_X = 382.0f + CONTROLLER_POS_X - centerX, TEXT_TRIANGLE_Y = 52.0f + CONTROLLER_POS_Y - centerY;
+ float TEXT_CIRCLE_X = 382.0f + CONTROLLER_POS_X - centerX, TEXT_CIRCLE_Y = 65.0f + CONTROLLER_POS_Y - centerY;
+ float TEXT_CROSS_X = 382.0f + CONTROLLER_POS_X - centerX, TEXT_CROSS_Y = 78.0f + CONTROLLER_POS_Y - centerY;
+ float TEXT_RSTICK_X = 382.0f + CONTROLLER_POS_X - centerX, TEXT_RSTICK_Y = 102.0f + CONTROLLER_POS_Y - centerY;
+ float TEXT_R3_X = 382.0f + CONTROLLER_POS_X - centerX, TEXT_R3_Y = 158.0f + CONTROLLER_POS_Y - centerY;
+ float TEXT_L3_X = 2.0f + CONTROLLER_POS_X - centerX, TEXT_L3_Y = 158.0f + CONTROLLER_POS_Y - centerY;
+ float TEXT_L2R2_X = 192.0f + CONTROLLER_POS_X - centerX, TEXT_L2R2_Y = 6.0f + CONTROLLER_POS_Y - centerY;
+
+ switch (m_PrefsControllerType)
+ {
+ case CONTROLLER_DUALSHOCK4:
+ TEXT_L1_Y -= 2.0f;
+ TEXT_R1_Y -= 2.0f;
+ TEXT_L1_Y_VEH = TEXT_L1_Y;
+ TEXT_TRIANGLE_Y += 0.0f;
+ TEXT_CIRCLE_Y += 4.0f;
+ TEXT_CROSS_Y += 7.0f;
+ TEXT_SQUARE_Y -= 3.0f;
+ TEXT_RSTICK_Y += 3.0f;
+ TEXT_R3_Y -= 30.0f;
+ TEXT_DPAD_Y -= 3.0f;
+ TEXT_LSTICK_Y += 3.0f;
+ TEXT_L3_Y -= 30.0f;
+ TEXT_SELECT_X -= 240.0f;
+ TEXT_SELECT_Y += 13.0f;
+ TEXT_START_X -= 198.0f;
+ TEXT_START_Y += 12.0f;
+ break;
+ case CONTROLLER_DUALSHOCK3:
+ TEXT_L1_Y += 4.0f;
+ TEXT_L1_Y_VEH = TEXT_L1_Y;
+ TEXT_TRIANGLE_Y += 5.0f;
+ TEXT_CIRCLE_Y += 9.0f;
+ TEXT_CROSS_Y += 15.0f;
+ TEXT_RSTICK_Y += 17.0f;
+ TEXT_R3_Y -= 17.0f;
+ TEXT_DPAD_Y += 11.0f;
+ TEXT_LSTICK_Y += 17.0f;
+ TEXT_L3_Y -= 17.0f;
+ TEXT_SELECT_X -= 238.0f;
+ TEXT_SELECT_Y -= 112.0f;
+ TEXT_START_X -= 214.0f;
+ TEXT_START_Y += 28.0f;
+ break;
+ case CONTROLLER_XBOXONE:
+ TEXT_L2_X -= 2.0f;
+ TEXT_R2_X += 2.0f;
+ TEXT_L1_Y += 15.0f;
+ TEXT_L1_Y_VEH = TEXT_L1_Y;
+ TEXT_R1_Y += 15.0f;
+ TEXT_TRIANGLE_Y += 4.0f;
+ TEXT_CIRCLE_Y += 4.0f;
+ TEXT_CROSS_Y += 4.0f;
+ TEXT_RSTICK_Y += 1.0f;
+ TEXT_R3_Y += 1.0f;
+ TEXT_DPAD_Y += 29.0f;
+ TEXT_LSTICK_Y -= 22.0f;
+ TEXT_L3_X -= 36.0f;
+ TEXT_L2R2_Y += 5.0f;
+ TEXT_SELECT_X += 4.0f;
+ break;
+ case CONTROLLER_XBOX360:
+ TEXT_L2_X += 8.0f;
+ TEXT_R2_X -= 8.0f;
+ TEXT_L1_Y += 15.0f;
+ TEXT_L1_Y_VEH = TEXT_L1_Y;
+ TEXT_R1_Y += 15.0f;
+ TEXT_TRIANGLE_Y += 4.0f;
+ TEXT_CIRCLE_Y += 4.0f;
+ TEXT_CROSS_Y += 4.0f;
+ TEXT_RSTICK_Y += 4.0f;
+ TEXT_R3_Y += 4.0f;
+ TEXT_DPAD_Y += 30.0f;
+ TEXT_LSTICK_Y -= 21.0f;
+ TEXT_L3_X -= 36.0f;
+ TEXT_L2R2_Y += 5.0f;
+ TEXT_SELECT_X += 3.0f;
+ break;
+ case CONTROLLER_NINTENDO_SWITCH:
+ TEXT_L1_Y += 5.0f;
+ TEXT_L1_Y_VEH = TEXT_L1_Y;
+ TEXT_R1_Y += 5.0f;
+ TEXT_TRIANGLE_Y += 3.0f;
+ TEXT_CIRCLE_Y += 3.0f;
+ TEXT_CROSS_Y += 3.0f;
+ TEXT_LSTICK_Y -= 23.0f;
+ TEXT_DPAD_Y += 25.0;
+ TEXT_RSTICK_Y += 1.0f;
+ TEXT_R3_Y += 1.0f;
+ break;
+ };
+
+ if (m_DisplayControllerOnFoot) {
+ switch (CPad::GetPad(0)->Mode) {
+ case 0:
+ CFont::SetCentreOn();
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_CWL"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_CWR"));
+ CFont::SetRightJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y)), TheText.Get("FEC_LOF"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_MOV"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_MOV"));
+ CFont::SetJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_TAR"));
+ CFont::SetJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_JUM"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_ENV"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_ATT"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_RUN"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_FPC"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_LB3"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y + 13.0f)), TheText.Get("FEC_R3"));
+ break;
+ case 1:
+ CFont::SetCentreOn();
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_CWL"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_CWR"));
+ CFont::SetRightJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y)), TheText.Get("FEC_LOF"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_CAM"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_MOV"));
+ CFont::SetJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_NA"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_TAR"));
+ CFont::SetJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_JUM"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_ENV"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_ATT"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_RUN"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_FPC"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_LB3"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y + 13.0f)), TheText.Get("FEC_R3"));
+ break;
+ case 2:
+ CFont::SetCentreOn();
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_CWL"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_CWR"));
+ CFont::SetRightJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y)), TheText.Get("FEC_ENV"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_MOV"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_MOV"));
+ CFont::SetJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_TAR"));
+ CFont::SetJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_JUM"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_LOF"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_RUN"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_ATT"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_FPC"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_LB3"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y + 13.0f)), TheText.Get("FEC_R3"));
+ break;
+ case 3:
+ CFont::SetCentreOn();
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_CWL"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_CWR"));
+ CFont::SetRightJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y)), TheText.Get("FEC_TAR"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_NA"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_MOV"));
+ CFont::SetJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_ATT"));
+ CFont::SetJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_JUM"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_ENV"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_LOF"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_RUN"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_FPC"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_LB3"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y + 13.0f)), TheText.Get("FEC_R3"));
+ break;
+ default:
+ return;
+ }
+ } else {
+ CFont::SetCentreOn();
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2R2_X)), MENU_Y(Y(TEXT_L2R2_Y)), TheText.Get("FEC_LB"));
+ switch (CPad::GetPad(0)->Mode) {
+ case 0:
+ CFont::SetCentreOn();
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_LL"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_LR"));
+ CFont::SetRightJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y_VEH)), TheText.Get("FEC_RSC"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_VES"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_VES"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L3_X)), MENU_Y(Y(TEXT_L3_Y)), TheText.Get("FEC_HO3"));
+ CFont::SetJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_HAB"));
+ CFont::SetJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_BRA"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_EXV"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_CAW"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_ACC"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_TUC"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_SM3"));
+ break;
+ case 1:
+ CFont::SetCentreOn();
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_LL"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_LR"));
+ CFont::SetRightJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y_VEH)), TheText.Get("FEC_HOR"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_CAM"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_VES"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L3_X)), MENU_Y(Y(TEXT_L3_Y)), TheText.Get("FEC_NA"));
+ CFont::SetJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_RSC"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_HAB"));
+ CFont::SetJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_BRA"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_EXV"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_CAW"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_ACC"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_TUC"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_SM3"));
+ break;
+ case 2:
+ CFont::SetCentreOn();
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_LL"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_LR"));
+ CFont::SetRightJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y_VEH)), TheText.Get("FEC_EXV"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_VES"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_VES"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L3_X)), MENU_Y(Y(TEXT_L3_Y)), TheText.Get("FEC_RS3"));
+ CFont::SetJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_HOR"));
+ CFont::SetJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_BRA"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_HAB"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_CAW"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_ACC"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_TUC"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_SM3"));
+ break;
+ case 3:
+ CFont::SetCentreOn();
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_LL"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_LR"));
+ CFont::SetRightJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y_VEH)), TheText.Get("FEC_HAB"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_TUC"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_VES"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L3_X)), MENU_Y(Y(TEXT_L3_Y)), TheText.Get("FEC_HO3"));
+ CFont::SetJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_CAW"));
+ CFont::SetJustifyOn(); // X
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_SMT"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_EXV"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_RSC"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_NA"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_ACC"));
+ CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_BRA"));
+ break;
+ default:
+ return;
+ }
+ }
+
+ CFont::SetDropShadowPosition(0); // X
+
+#undef X
+#undef Y
+#elif defined RT_CONTROLLERS
const float scale = 0.9f;
const float CONTROLLER_SIZE_X = 384.0f;
const float CONTROLLER_SIZE_Y = 384.0f;
diff --git a/src/core/Frontend.h b/src/core/Frontend.h
index b69ad97a..ea48dd28 100644
--- a/src/core/Frontend.h
+++ b/src/core/Frontend.h
@@ -758,13 +758,25 @@ public:
CONTROLLER_DUALSHOCK2 = 0,
CONTROLLER_DUALSHOCK3,
CONTROLLER_DUALSHOCK4,
-#ifndef RT
+#ifndef RT_CONTROLLERS
CONTROLLER_XBOX360,
#endif
CONTROLLER_XBOXONE,
CONTROLLER_NINTENDO_SWITCH,
+ NUM_CONTROLLER_TYPES,
};
+#ifdef PSP2
+ #define NUM_MENU_CONTROLLER_TYPES (CMenuManager::CONTROLLER_DUALSHOCK4 + 1) // the Vita itself, plus the DS3/DS4 a PSTV can pair with
+ #define DEFAULT_CONTROLLER_TYPE CMenuManager::CONTROLLER_DUALSHOCK2
+#elif defined __SWITCH__
+ #define NUM_MENU_CONTROLLER_TYPES CMenuManager::NUM_CONTROLLER_TYPES
+ #define DEFAULT_CONTROLLER_TYPE CMenuManager::CONTROLLER_NINTENDO_SWITCH
+#else
+ #define NUM_MENU_CONTROLLER_TYPES CMenuManager::NUM_CONTROLLER_TYPES
+ #define DEFAULT_CONTROLLER_TYPE CMenuManager::CONTROLLER_XBOXONE
+#endif
+
static int8 m_PrefsControllerType;
#endif
diff --git a/src/core/Game.cpp b/src/core/Game.cpp
index b3dd1eda..40b3a5b1 100644
--- a/src/core/Game.cpp
+++ b/src/core/Game.cpp
@@ -244,6 +244,14 @@ CGame::InitialiseRenderWare(void)
#endif
#ifdef LIBRW
+// librw-vita predates the envMap* knobs and only exposes modulateEnvMap.
+#ifdef PSP2
+#ifdef PS2_MATFX
+ rw::MatFX::modulateEnvMap = true;
+#else
+ rw::MatFX::modulateEnvMap = false;
+#endif
+#else
#ifdef PS2_MATFX
rw::MatFX::envMapApplyLight = true;
rw::MatFX::envMapUseMatColor = true;
@@ -255,6 +263,7 @@ CGame::InitialiseRenderWare(void)
#endif
rw::RGBA envcol = { 128, 128, 128, 255 };
rw::MatFX::envMapColor = envcol;
+#endif
#else
#ifdef PS2_MATFX
ReplaceMatFxCallback();
diff --git a/src/core/MenuScreensCustom.cpp b/src/core/MenuScreensCustom.cpp
index 292a5ace..dbdfc444 100644
--- a/src/core/MenuScreensCustom.cpp
+++ b/src/core/MenuScreensCustom.cpp
@@ -32,6 +32,10 @@
// Menu screens array is at the bottom of the file.
+#ifdef PSP2
+extern bool useRearpad; // touch input: front panel or rear, see crossplatform.cpp
+#endif
+
#ifdef PC_MENU
#ifdef CUSTOM_FRONTEND_OPTIONS
@@ -42,7 +46,9 @@
#define VIDEOMODE_SELECTOR
#endif
-#ifdef MULTISAMPLING
+// Not on PSP2: vglInitExtended fixes the level at 4x, so the entry could never
+// change anything, and stepping through it used to hang the game.
+#if defined(MULTISAMPLING) && !defined(PSP2)
#define MULTISAMPLING_SELECTOR MENUACTION_CFO_DYNAMIC, "FED_AAS", { new CCFODynamic((int8*)&FrontEndMenuManager.m_nPrefsMSAALevel, "Graphics", "MultiSampling", MultiSamplingDraw, MultiSamplingButtonPress) },
#else
#define MULTISAMPLING_SELECTOR
@@ -96,7 +102,7 @@
#endif
#ifdef GAMEPAD_MENU
- #define SELECT_CONTROLLER_TYPE MENUACTION_CFO_SELECT, "FEC_TYP", { new CCFOSelect((int8*)&CMenuManager::m_PrefsControllerType, "Controller", "Type", controllerTypes, ARRAY_SIZE(controllerTypes), false, ControllerTypeAfterChange) },
+ #define SELECT_CONTROLLER_TYPE MENUACTION_CFO_SELECT, "FEC_TYP", { new CCFOSelect((int8*)&CMenuManager::m_PrefsControllerType, "Controller", "Type", controllerTypes, NUM_MENU_CONTROLLER_TYPES, false, ControllerTypeAfterChange) },
#else
#define SELECT_CONTROLLER_TYPE
#endif
@@ -242,7 +248,9 @@ void MultiSamplingButtonPress(int8 action) {
int i = 0;
int maxAA = RwD3D8EngineGetMaxMultiSamplingLevels();
- while (maxAA != 1) {
+ // > 1, not != 1: the PSP2 stub reports 0 levels, and 0 >> 1 stays 0,
+ // so the original condition never terminates and hangs the game.
+ while (maxAA > 1) {
i++;
maxAA >>= 1;
}
@@ -388,8 +396,10 @@ void DetectJoystickGoBack() {
#endif
#ifdef GAMEPAD_MENU
+// Stays parallel to the controller enum in Frontend.h - it is indexed by type, not
+// iterated. The menu offers only the first NUM_MENU_CONTROLLER_TYPES of them.
const char* controllerTypes[] = { "FEC_DS2", "FEC_DS3", "FEC_DS4",
-#ifndef RT
+#ifndef RT_CONTROLLERS
"FEC_360",
#endif
"FEC_ONE", "FEC_NSW" };
@@ -422,14 +432,21 @@ CMenuScreenCustom aScreens[MENUPAGES] = {
},
// MENUPAGE_CONTROLLER_SETTINGS = 4
-#if defined(GAMEPAD_MENU) && !defined(GTA_HANDHELD)
+#if defined(GAMEPAD_MENU) && !defined(GTA_HANDHELD) && !defined(PSP2)
{ "FET_AGS", MENUPAGE_CONTROLLER_PC, MENUPAGE_CONTROLLER_PC, nil, nil,
+#elif defined(PSP2)
+ // No gamepad-selection page here: there is exactly one pad, and it gets its own
+ // entry below for choosing which touch panel to use.
+ { "FET_CON", MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, nil, nil,
#else
{ "FET_AGS", MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, nil, nil,
#endif
MENUACTION_CTRLCONFIG, "FEC_CCF", { nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_SETTINGS },
MENUACTION_CTRLDISPLAY, "FEC_CDP", { nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_SETTINGS },
INVERT_PAD_SELECTOR
+#ifdef PSP2
+ MENUACTION_CFO_SELECT, "PSP2", { new CCFOSelect((int8*)&useRearpad, "Controller", "Rearpad", off_on, 2, false) },
+#endif
MENUACTION_CTRLVIBRATION, "FEC_VIB", { nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_SETTINGS },
SELECT_CONTROLLER_TYPE
MENUACTION_CHANGEMENU, "FEDS_TB", { nil, SAVESLOT_NONE, MENUPAGE_NONE },
diff --git a/src/core/Pad.cpp b/src/core/Pad.cpp
index 005b18b2..f8762b81 100644
--- a/src/core/Pad.cpp
+++ b/src/core/Pad.cpp
@@ -769,7 +769,7 @@ void CPad::StartShake_Train(float fX, float fY)
static bool MenuUsesNintendoLayout()
{
return CMenuManager::m_PrefsControllerType == CMenuManager::CONTROLLER_NINTENDO_SWITCH
-#ifdef RT
+#ifdef RT_CONTROLLERS
|| CMenuManager::m_PrefsControllerType == CMenuManager::CONTROLLER_DUALSHOCK2 // RT: DS2 slot is a Switch Pro Controller
#endif
;
@@ -1178,6 +1178,18 @@ void CPad::Update(int16 pad)
{
OldState = NewState;
+#ifdef PSP2
+ if ( ShakeDur )
+ {
+ ShakeDur = Max((int32_t)ShakeDur - (int32_t)CTimer::GetTimeStepInMilliseconds(), 0);
+
+ SceCtrlActuator handle;
+ handle.small = 0;
+ handle.large = ShakeDur == 0 ? 0 : (unsigned char)ShakeFreq;
+ sceCtrlSetActuator(pad + 1, &handle);
+ }
+#endif
+
#ifdef GTA_PS2
bObsoleteControllerMessage = false;
diff --git a/src/core/common.h b/src/core/common.h
index 0d0528b1..cbc82c58 100644
--- a/src/core/common.h
+++ b/src/core/common.h
@@ -1,5 +1,11 @@
#pragma once
+#ifdef PSP2
+extern "C"{
+#include
+};
+#endif
+
#define _CRT_SECURE_NO_WARNINGS
#define _USE_MATH_DEFINES
#pragma warning(disable: 4244) // int to float
diff --git a/src/core/config.h b/src/core/config.h
index ddd2643b..56c6df35 100644
--- a/src/core/config.h
+++ b/src/core/config.h
@@ -206,10 +206,20 @@ enum Config {
// Every other platform builds vanilla. Platform necessities (audio/loading tweaks)
// are gated on __SWITCH__ separately, so a vanilla NX build still keeps those.
// Kept above VANILLA_DEFINES so the tunables below exist in every configuration.
-#if defined(__SWITCH__) && !defined(NO_RT)
+// The Vita ships the same 10thAE resources, and its menu layout is drawn against
+// them, so it is an RT build too. The controller half of RT stays Switch-only -
+// see the RT && __SWITCH__ gates around the pad slots.
+#if (defined(__SWITCH__) || defined(PSP2)) && !defined(NO_RT)
#define RT
#endif
+// The controller half of RT - dropping the X360 slot and repurposing DS2 as a
+// Switch Pro Controller - only makes sense on the Switch. The Vita has its own
+// three pads (DS2 is the console itself) and keeps the upstream slots.
+#if defined(RT) && defined(__SWITCH__)
+#define RT_CONTROLLERS
+#endif
+
// RT tunables, centralized so shared code stays readable. RT off = upstream values.
#ifdef RT
#define RT_RADAR_SCALE 1.3f // enlarge the minimap by 30%
@@ -318,22 +328,29 @@ enum Config {
#define USE_TXD_CDIMAGE // generate and load textures from txd.img
#define PS2_ALPHA_TEST // emulate ps2 alpha test
#define IMPROVED_VIDEOMODE // save and load videomode parameters instead of a magic number
-#ifndef __SWITCH__
-#define DISABLE_LOADING_SCREEN // disable the loading screen which vastly improves the loading time (kept on Switch to show it)
+#if !defined(__SWITCH__) && !defined(PSP2)
+#define DISABLE_LOADING_SCREEN // disable the loading screen which vastly improves the loading time (both handhelds keep it, to show it)
#endif
#ifdef DISABLE_LOADING_SCREEN
// enable the PC splash
#undef RANDOMSPLASH
#endif
#define DISABLE_VSYNC_ON_TEXTURE_CONVERSION // make texture conversion work faster by disabling vsync
+#ifndef PSP2
#define ANISOTROPIC_FILTERING // set all textures to max anisotropic filtering
+#endif
//#define USE_TEXTURE_POOL
#ifdef LIBRW
#define EXTENDED_COLOURFILTER // more options for colour filter (replaces mblur)
+// Not on PSP2: librw-vita is an old fork with no setupVertexInput, no three-argument
+// Shader::create and no UNIFORM_VEC4, so the Neo pipelines and the droplets cannot
+// build against it. They would also need Cg versions of every shader.
+#ifndef PSP2
#define EXTENDED_PIPELINES // custom render pipelines (includes Neo)
#define SCREEN_DROPLETS // neo water droplets
#define NEW_RENDERER // leeds-like world rendering, needs librw
#endif
+#endif
#define FIX_SPRITES // fix sprites aspect ratio(moon, coronas, particle etc)
@@ -461,7 +478,11 @@ enum Config {
#define PAUSE_RADIO_IN_FRONTEND // pause radio when game is paused
#define ATTACH_RELEASING_SOUNDS_TO_ENTITIES // sounds would follow ped and vehicles coordinates if not being queued otherwise
#define USE_TIME_SCALE_FOR_AUDIO // slow down/speed up sounds according to the speed of the game
+// Not on PSP2: std::thread cannot start here, and with -fno-exceptions the
+// system_error it throws goes straight to terminate().
+#ifndef PSP2
#define MULTITHREADED_AUDIO // for streams. requires C++11 or later
+#endif
#ifdef AUDIO_OPUS
#define AUDIO_OAL_USE_OPUS // enable support of opus files
diff --git a/src/core/main.cpp b/src/core/main.cpp
index 2a0a77ca..3da64a33 100644
--- a/src/core/main.cpp
+++ b/src/core/main.cpp
@@ -378,6 +378,11 @@ RwGrabScreen(RwCamera *camera, RwChar *filename)
#define TILE_WIDTH 576
#define TILE_HEIGHT 432
+#ifdef PSP2
+extern GLuint fxfb;
+bool using_fbo = false;
+#endif
+
void
DoRWStuffEndOfFrame(void)
{
@@ -385,6 +390,19 @@ DoRWStuffEndOfFrame(void)
CDebug::DebugDisplayTextBuffer();
FlushObrsPrintfs();
RwCameraEndUpdate(Scene.camera);
+
+#if defined(PSP2) && defined(EXTENDED_COLOURFILTER)
+ // Arm the next frame's capture: bind our FBO once CPostFX has asked for it, so
+ // the scene renders into fxraster and the colour filter can sample it.
+ if(CPostFX::NeedBackBuffer()){
+ if (using_fbo) {
+ glBindFramebuffer(GL_FRAMEBUFFER, fxfb);
+ using_fbo = false;
+ } else
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ }
+#endif
+
RsCameraShowRaster(Scene.camera);
#ifndef MASTER
char s[48];
@@ -1496,7 +1514,12 @@ Render2dStuff(void)
firstPersonWeapon = true;
// Draw black border for sniper and rocket launcher
+#ifdef FIX_BUGS
+ // The rocket launcher has no scope, so the border just blacks out the screen.
+ if(weaponType == WEAPONTYPE_SNIPERRIFLE && firstPersonWeapon){
+#else
if((weaponType == WEAPONTYPE_SNIPERRIFLE || weaponType == WEAPONTYPE_ROCKETLAUNCHER) && firstPersonWeapon){
+#endif
CRGBA black(0, 0, 0, 255);
// top and bottom strips
diff --git a/src/core/re3.cpp b/src/core/re3.cpp
index 02ed39ca..e7e394fb 100644
--- a/src/core/re3.cpp
+++ b/src/core/re3.cpp
@@ -184,7 +184,14 @@ CustomFrontendOptionsPopulate(void)
#define MINI_CASE_SENSITIVE
#include "ini.h"
+#ifdef PSP2
+// mINI opens the file with std::ifstream/ofstream, which bypasses the fcaseopen/
+// casepath shims that resolve relative paths against the data dir, so a bare
+// "re3.ini" never opens here and settings would not persist.
+mINI::INIFile ini(PSP2_DATA_PATH "/re3.ini");
+#else
mINI::INIFile ini("re3.ini");
+#endif
mINI::INIStructure cfg;
bool ReadIniIfExists(const char *cat, const char *key, uint32 *out)
diff --git a/src/extras/postfx.cpp b/src/extras/postfx.cpp
index cb02822a..0634026a 100644
--- a/src/extras/postfx.cpp
+++ b/src/extras/postfx.cpp
@@ -13,6 +13,11 @@
#include "MBlur.h"
#include "postfx.h"
+#ifdef PSP2
+GLuint fxraster = 0xDEADBEEF, fxfb;
+extern bool using_fbo;
+#endif
+
RwRaster *CPostFX::pFrontBuffer;
RwRaster *CPostFX::pBackBuffer;
bool CPostFX::bJustInitialised;
@@ -54,6 +59,10 @@ CPostFX::Open(RwCamera *cam)
uint32 width = Pow(2.0f, int32(log2(RwRasterGetWidth (RwCameraGetRaster(cam))))+1);
uint32 height = Pow(2.0f, int32(log2(RwRasterGetHeight(RwCameraGetRaster(cam))))+1);
uint32 depth = RwRasterGetDepth(RwCameraGetRaster(cam));
+#ifdef PSP2
+ width = RwRasterGetWidth(RwCameraGetRaster(cam));
+ height = RwRasterGetHeight(RwCameraGetRaster(cam));
+#endif
pFrontBuffer = RwRasterCreate(width, height, depth, rwRASTERTYPECAMERATEXTURE);
pBackBuffer = RwRasterCreate(width, height, depth, rwRASTERTYPECAMERATEXTURE);
bJustInitialised = true;
@@ -151,22 +160,39 @@ CPostFX::Open(RwCamera *cam)
contrast_PS = rw::d3d::createPixelShader(contrastPS_cso);
#endif
#ifdef RW_OPENGL
+#ifdef PSP2
+ #include "shaders/im2d_v_cg.inc"
+#endif
using namespace rw::gl3;
{
+#ifdef PSP2
+#include "shaders/colourfilterIII_f_cg.inc"
+ const char *vs[] = { im2d_v_cg_src, nil };
+ const char *fs[] = { colourfilterIII_f_cg_src, nil };
+ colourFilterIII = Shader::create(vs, fs, true);
+#else
#include "shaders/obj/im2d_vert.inc"
#include "shaders/obj/colourfilterIII_frag.inc"
const char *vs[] = { shaderDecl, header_vert_src, im2d_vert_src, nil };
const char *fs[] = { shaderDecl, header_frag_src, colourfilterIII_frag_src, nil };
colourFilterIII = Shader::create(vs, fs);
+#endif
assert(colourFilterIII);
}
{
+#ifdef PSP2
+#include "shaders/contrast_f_cg.inc"
+ const char *vs[] = { im2d_v_cg_src, nil };
+ const char *fs[] = { contrast_f_cg_src, nil };
+ contrast = Shader::create(vs, fs, true);
+#else
#include "shaders/obj/im2d_vert.inc"
#include "shaders/obj/contrast_frag.inc"
const char *vs[] = { shaderDecl, header_vert_src, im2d_vert_src, nil };
const char *fs[] = { shaderDecl, header_frag_src, contrast_frag_src, nil };
contrast = Shader::create(vs, fs);
+#endif
assert(contrast);
}
@@ -305,6 +331,14 @@ CPostFX::RenderOverlayShader(RwCamera *cam, int32 r, int32 g, int32 b, int32 a)
glUniform4fv(colourFilterIII->uniformLocations[u_blurcolor], 1, blurcolors);
#endif
}
+#ifdef PSP2
+ // The scene lives in our own FBO texture here, not in pBackBuffer.
+ glBindTexture(GL_TEXTURE_2D, fxraster);
+ RwIm2DVertexSetIntRGBA(&Vertex[0], 255, 255, 255, 255);
+ RwIm2DVertexSetIntRGBA(&Vertex[1], 255, 255, 255, 255);
+ RwIm2DVertexSetIntRGBA(&Vertex[2], 255, 255, 255, 255);
+ RwIm2DVertexSetIntRGBA(&Vertex[3], 255, 255, 255, 255);
+#endif
RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6);
#ifdef RW_D3D9
rw::d3d::im2dOverridePS = nil;
@@ -432,8 +466,11 @@ CPostFX::Render(RwCamera *cam, uint32 red, uint32 green, uint32 blue, uint32 blu
assert(pFrontBuffer);
assert(pBackBuffer);
+ // PSP2 captures the scene through its own FBO a frame ahead instead.
+#ifndef PSP2
if(NeedBackBuffer())
GetBackBuffer(cam);
+#endif
DefinedState();
@@ -445,24 +482,40 @@ CPostFX::Render(RwCamera *cam, uint32 red, uint32 green, uint32 blue, uint32 blu
if(type == MOTION_BLUR_SNIPER){
if(!bJustInitialised)
RenderOverlaySniper(cam, red, green, blue, blur);
- }else switch(EffectSwitch){
- case POSTFX_OFF:
- // no actual rendering here
- break;
- case POSTFX_SIMPLE:
- RenderOverlaySimple(cam, red, green, blue, blur);
- break;
- case POSTFX_NORMAL:
- if(MotionBlurOn){
- if(!bJustInitialised)
- RenderOverlayBlur(cam, red, green, blue, blur);
- }else{
- RenderOverlayShader(cam, red, green, blue, blur);
+ }else{
+#if defined(PSP2) && defined(EXTENDED_COLOURFILTER)
+ if (NeedBackBuffer()) {
+ if(fxraster == 0xDEADBEEF){
+ glGenTextures(1, &fxraster);
+ glBindTexture(GL_TEXTURE_2D, fxraster);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 960, 544, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ glGenFramebuffers(1, &fxfb);
+ glBindFramebuffer(GL_FRAMEBUFFER, fxfb);
+ glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, fxraster, 0);
+ }
+ using_fbo = true;
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ }
+#endif
+ switch(EffectSwitch){
+ case POSTFX_OFF:
+ // no actual rendering here
+ break;
+ case POSTFX_SIMPLE:
+ RenderOverlaySimple(cam, red, green, blue, blur);
+ break;
+ case POSTFX_NORMAL:
+ if(MotionBlurOn){
+ if(!bJustInitialised)
+ RenderOverlayBlur(cam, red, green, blue, blur);
+ }else{
+ RenderOverlayShader(cam, red, green, blue, blur);
+ }
+ break;
+ case POSTFX_MOBILE:
+ RenderOverlayShader(cam, red, green, blue, blur);
+ break;
}
- break;
- case POSTFX_MOBILE:
- RenderOverlayShader(cam, red, green, blue, blur);
- break;
}
// TODO? maybe we want this even without motion blur on sometimes?
diff --git a/src/extras/shaders/colourfilterIII_f.cg b/src/extras/shaders/colourfilterIII_f.cg
new file mode 100644
index 00000000..a95f9d12
--- /dev/null
+++ b/src/extras/shaders/colourfilterIII_f.cg
@@ -0,0 +1,38 @@
+#define u_fogStart (u_fogData.x)
+#define u_fogEnd (u_fogData.y)
+#define u_fogRange (u_fogData.z)
+#define u_fogDisable (u_fogData.w)
+
+void DoAlphaTest(float a, half2 u_alphaRef)
+{
+ if(a < u_alphaRef.x || a >= u_alphaRef.y)
+ discard;
+}
+
+float4 main(
+ half4 v_color : COLOR0,
+ half2 v_tex0 : TEXCOORD0,
+ fixed v_fog : FOG,
+ uniform half4 u_fogColor,
+ uniform half2 u_alphaRef,
+ uniform half4 u_blurcolor,
+ uniform sampler2D tex0
+) {
+ half a = u_blurcolor.a;
+ half4 dst = v_color*tex2D(tex0, half2(v_tex0.x, 1.0-v_tex0.y));
+ dst.rgb = lerp(u_fogColor.rgb, dst.rgb, v_fog);
+ DoAlphaTest(dst.a, u_alphaRef);
+
+ half4 prev = dst;
+ for (int i = 0; i < 5; i++){
+ half4 tmp = dst*(1.0-a) + prev*u_blurcolor*a;
+ prev = clamp(tmp, 0.0, 1.0);
+ }
+ half4 color;
+ color.rgb = prev.rgb;
+ color.a = 1.0f;
+
+ return color;
+}
+
+
diff --git a/src/extras/shaders/colourfilterIII_f_cg.inc b/src/extras/shaders/colourfilterIII_f_cg.inc
new file mode 100644
index 00000000..5c65589e
--- /dev/null
+++ b/src/extras/shaders/colourfilterIII_f_cg.inc
@@ -0,0 +1,40 @@
+const char *colourfilterIII_f_cg_src =
+"#define u_fogStart (u_fogData.x)\n"
+"#define u_fogEnd (u_fogData.y)\n"
+"#define u_fogRange (u_fogData.z)\n"
+"#define u_fogDisable (u_fogData.w)\n"
+
+"void DoAlphaTest(float a, half2 u_alphaRef)\n"
+"{\n"
+" if(a < u_alphaRef.x || a >= u_alphaRef.y)\n"
+" discard;\n"
+"}\n"
+
+"float4 main(\n"
+" half4 v_color : COLOR0,\n"
+" half2 v_tex0 : TEXCOORD0,\n"
+" fixed v_fog : FOG,\n"
+" uniform half4 u_fogColor,\n"
+" uniform half2 u_alphaRef,\n"
+" uniform half4 u_blurcolor,\n"
+" uniform sampler2D tex0\n"
+") {\n"
+" half a = u_blurcolor.a;\n"
+" half4 dst = v_color*tex2D(tex0, half2(v_tex0.x, 1.0-v_tex0.y));\n"
+" dst.rgb = lerp(u_fogColor.rgb, dst.rgb, v_fog);\n"
+" DoAlphaTest(dst.a, u_alphaRef);\n"
+
+" half4 prev = dst;\n"
+" for (int i = 0; i < 5; i++){\n"
+" half4 tmp = dst*(1.0-a) + prev*u_blurcolor*a;\n"
+" prev = clamp(tmp, 0.0, 1.0);\n"
+" }\n"
+" half4 color;\n"
+" color.rgb = prev.rgb;\n"
+" color.a = 1.0f;\n"
+
+" return color;\n"
+"}\n"
+
+
+;
diff --git a/src/extras/shaders/contrast_f.cg b/src/extras/shaders/contrast_f.cg
new file mode 100644
index 00000000..c1ed26e3
--- /dev/null
+++ b/src/extras/shaders/contrast_f.cg
@@ -0,0 +1,33 @@
+#define u_fogStart (u_fogData.x)
+#define u_fogEnd (u_fogData.y)
+#define u_fogRange (u_fogData.z)
+#define u_fogDisable (u_fogData.w)
+
+void DoAlphaTest(float a, half2 u_alphaRef)
+{
+ if(a < u_alphaRef.x || a >= u_alphaRef.y)
+ discard;
+}
+
+float4 main(
+ half4 v_color : COLOR0,
+ half2 v_tex0 : TEXCOORD0,
+ fixed v_fog : FOG,
+ uniform half4 u_fogColor,
+ uniform half2 u_alphaRef,
+ uniform float3 u_contrastAdd,
+ uniform float3 u_contrastMult,
+ uniform sampler2D tex0
+) {
+ half4 dst = v_color*tex2D(tex0, half2(v_tex0.x, 1.0-v_tex0.y));
+ dst.rgb = lerp(u_fogColor.rgb, dst.rgb, v_fog);
+ DoAlphaTest(dst.a, u_alphaRef);
+
+ half4 color;
+ color.rgb = dst.rgb*u_contrastMult + u_contrastAdd;
+ color.a = 1.0f;
+
+ return color;
+}
+
+
diff --git a/src/extras/shaders/contrast_f_cg.inc b/src/extras/shaders/contrast_f_cg.inc
new file mode 100644
index 00000000..ef1a892c
--- /dev/null
+++ b/src/extras/shaders/contrast_f_cg.inc
@@ -0,0 +1,35 @@
+const char *contrast_f_cg_src =
+"#define u_fogStart (u_fogData.x)\n"
+"#define u_fogEnd (u_fogData.y)\n"
+"#define u_fogRange (u_fogData.z)\n"
+"#define u_fogDisable (u_fogData.w)\n"
+
+"void DoAlphaTest(float a, half2 u_alphaRef)\n"
+"{\n"
+" if(a < u_alphaRef.x || a >= u_alphaRef.y)\n"
+" discard;\n"
+"}\n"
+
+"float4 main(\n"
+" half4 v_color : COLOR0,\n"
+" half2 v_tex0 : TEXCOORD0,\n"
+" fixed v_fog : FOG,\n"
+" uniform half4 u_fogColor,\n"
+" uniform half2 u_alphaRef,\n"
+" uniform float3 u_contrastAdd,\n"
+" uniform float3 u_contrastMult,\n"
+" uniform sampler2D tex0\n"
+") {\n"
+" half4 dst = v_color*tex2D(tex0, half2(v_tex0.x, 1.0-v_tex0.y));\n"
+" dst.rgb = lerp(u_fogColor.rgb, dst.rgb, v_fog);\n"
+" DoAlphaTest(dst.a, u_alphaRef);\n"
+
+" half4 color;\n"
+" color.rgb = dst.rgb*u_contrastMult + u_contrastAdd;\n"
+" color.a = 1.0f;\n"
+
+" return color;\n"
+"}\n"
+
+
+;
diff --git a/src/extras/shaders/im2d_v.cg b/src/extras/shaders/im2d_v.cg
new file mode 100644
index 00000000..28c3252b
--- /dev/null
+++ b/src/extras/shaders/im2d_v.cg
@@ -0,0 +1,35 @@
+#define u_fogStart (u_fogData.x)
+#define u_fogEnd (u_fogData.y)
+#define u_fogRange (u_fogData.z)
+#define u_fogDisable (u_fogData.w)
+
+#define MAX_LIGHTS 8
+
+#define surfAmbient (u_surfProps.x)
+#define surfSpecular (u_surfProps.y)
+#define surfDiffuse (u_surfProps.z)
+
+fixed DoFog(float w, half4 u_fogData)
+{
+ return clamp((w - u_fogEnd)*u_fogRange, u_fogDisable, 1.0);
+}
+
+void main(
+ float4 in_pos,
+ half4 in_color,
+ half2 in_tex0,
+ uniform half4 u_fogData,
+ uniform float4 u_xform,
+ half4 out v_color : COLOR0,
+ half2 out v_tex0 : TEXCOORD0,
+ fixed out v_fog : FOG,
+ float4 out gl_Position : POSITION
+) {
+ gl_Position = in_pos;
+ gl_Position.w = 1.0;
+ gl_Position.xy = gl_Position.xy * u_xform.xy + u_xform.zw;
+ v_fog = DoFog(gl_Position.z, u_fogData);
+ gl_Position.xyz *= gl_Position.w;
+ v_color = in_color;
+ v_tex0 = in_tex0;
+}
diff --git a/src/extras/shaders/im2d_v_cg.inc b/src/extras/shaders/im2d_v_cg.inc
new file mode 100644
index 00000000..133343e9
--- /dev/null
+++ b/src/extras/shaders/im2d_v_cg.inc
@@ -0,0 +1,37 @@
+const char *im2d_v_cg_src =
+"#define u_fogStart (u_fogData.x)\n"
+"#define u_fogEnd (u_fogData.y)\n"
+"#define u_fogRange (u_fogData.z)\n"
+"#define u_fogDisable (u_fogData.w)\n"
+
+"#define MAX_LIGHTS 8\n"
+
+"#define surfAmbient (u_surfProps.x)\n"
+"#define surfSpecular (u_surfProps.y)\n"
+"#define surfDiffuse (u_surfProps.z)\n"
+
+"fixed DoFog(float w, half4 u_fogData)\n"
+"{\n"
+" return clamp((w - u_fogEnd)*u_fogRange, u_fogDisable, 1.0);\n"
+"}\n"
+
+"void main(\n"
+" float4 in_pos,\n"
+" half4 in_color,\n"
+" half2 in_tex0,\n"
+" uniform half4 u_fogData,\n"
+" uniform float4 u_xform,\n"
+" half4 out v_color : COLOR0,\n"
+" half2 out v_tex0 : TEXCOORD0,\n"
+" fixed out v_fog : FOG,\n"
+" float4 out gl_Position : POSITION\n"
+") {\n"
+" gl_Position = in_pos;\n"
+" gl_Position.w = 1.0;\n"
+" gl_Position.xy = gl_Position.xy * u_xform.xy + u_xform.zw;\n"
+" v_fog = DoFog(gl_Position.z, u_fogData);\n"
+" gl_Position.xyz *= gl_Position.w;\n"
+" v_color = in_color;\n"
+" v_tex0 = in_tex0;\n"
+"}\n"
+;
diff --git a/src/fakerw/fake.cpp b/src/fakerw/fake.cpp
index 6dfebb39..38447751 100644
--- a/src/fakerw/fake.cpp
+++ b/src/fakerw/fake.cpp
@@ -299,12 +299,40 @@ RwTextureAddressMode RwTextureGetAddressingV(const RwTexture *texture);
// TODO
void _rwD3D8TexDictionaryEnableRasterFormatConversion(bool enable) { }
+#ifdef PSP2
+// librw-vita predates Raster::convertTexToCurrentPlatform, so do it by hand:
+// round-trip through an Image, which also drops the palette the Vita cannot use.
+static rw::Raster*
+ConvertTexRaster(rw::Raster *ras)
+{
+ using namespace rw;
+
+ if(ras->platform == rw::platform)
+ return ras;
+ // compatible platforms
+ if(ras->platform == PLATFORM_D3D8 && rw::platform == PLATFORM_D3D9 ||
+ ras->platform == PLATFORM_D3D9 && rw::platform == PLATFORM_D3D8)
+ return ras;
+
+ Image *img = ras->toImage();
+ ras->destroy();
+ img->unpalettize();
+ ras = Raster::createFromImage(img);
+ img->destroy();
+ return ras;
+}
+#endif
+
// hack for reading native textures
RwBool rwNativeTextureHackRead(RwStream *stream, RwTexture **tex, RwInt32 size)
{
*tex = Texture::streamReadNative(stream);
#ifdef LIBRW
+#ifdef PSP2
+ (*tex)->raster = ConvertTexRaster((*tex)->raster);
+#else
(*tex)->raster = rw::Raster::convertTexToCurrentPlatform((*tex)->raster);
+#endif
#endif
return *tex != nil;
}
@@ -550,6 +578,7 @@ static void *reallocWrap(void *p, size_t sz, uint32 hint) { return real_realloc(
// WARNING: unused parameters
RwBool RwEngineInit(RwMemoryFunctions *memFuncs, RwUInt32 initFlags, RwUInt32 resArenaSize) {
+#ifndef PSP2
if(memFuncs){
real_malloc = memFuncs->rwmalloc;
real_realloc = memFuncs->rwrealloc;
@@ -560,6 +589,10 @@ RwBool RwEngineInit(RwMemoryFunctions *memFuncs, RwUInt32 initFlags, RwUInt32 re
}else{
Engine::init(nil);
}
+#else
+ // librw-vita's Engine::init takes no memory functions.
+ Engine::init();
+#endif
return true;
}
// TODO: this is platform dependent
@@ -601,8 +634,20 @@ void RwD3D8EngineSetRefreshRate(RwUInt32 refreshRate) {}
RwBool RwD3D8DeviceSupportsDXTTexture(void) { return true; }
-void RwD3D8EngineSetMultiSamplingLevels(RwUInt32 level) { Engine::setMultiSamplingLevels(level); }
-RwUInt32 RwD3D8EngineGetMaxMultiSamplingLevels(void) { return Engine::getMaxMultiSamplingLevels(); }
+// librw-vita has no multisampling controls - the level is fixed when vitaGL starts.
+// Reporting 0 levels is what keeps the menu entry out; see MenuScreensCustom.
+void RwD3D8EngineSetMultiSamplingLevels(RwUInt32 level) {
+#ifndef PSP2
+ Engine::setMultiSamplingLevels(level);
+#endif
+}
+RwUInt32 RwD3D8EngineGetMaxMultiSamplingLevels(void) {
+#ifndef PSP2
+ return Engine::getMaxMultiSamplingLevels();
+#else
+ return 0;
+#endif
+}
RpMaterial *RpMaterialCreate(void) { return Material::create(); }
@@ -1004,7 +1049,15 @@ RwBool RtCharsetDestroy(RtCharset * charSet) { charSet->destroy(); return
#include
+#ifdef PSP2
+// librw-vita has no anisotropy plugin. Reporting zero keeps callers away from it.
+RwInt8 RpAnisotGetMaxSupportedMaxAnisotropy(void) { return 0; }
+RwTexture *RpAnisotTextureSetMaxAnisotropy(RwTexture *tex, RwInt8 val) { return tex; }
+RwInt8 RpAnisotTextureGetMaxAnisotropy(RwTexture *tex) { return 0; }
+RwBool RpAnisotPluginAttach(void) { return false; }
+#else
RwInt8 RpAnisotGetMaxSupportedMaxAnisotropy(void) { return rw::getMaxSupportedMaxAnisotropy(); }
RwTexture *RpAnisotTextureSetMaxAnisotropy(RwTexture *tex, RwInt8 val) { tex->setMaxAnisotropy(val); return tex; }
RwInt8 RpAnisotTextureGetMaxAnisotropy(RwTexture *tex) { return tex->getMaxAnisotropy(); }
RwBool RpAnisotPluginAttach(void) { rw::registerAnisotropyPlugin(); return true; }
+#endif
diff --git a/src/math/Vector.cpp b/src/math/Vector.cpp
index ee76e555..bce68615 100644
--- a/src/math/Vector.cpp
+++ b/src/math/Vector.cpp
@@ -3,6 +3,9 @@
void
CVector::Normalise(void)
{
+#ifdef PSP2
+ normalize3_neon(&this->x, &this->x);
+#else
float sq = MagnitudeSqr();
if (sq > 0.0f) {
float invsqrt = RecipSqrt(sq);
@@ -11,6 +14,7 @@ CVector::Normalise(void)
z *= invsqrt;
} else
x = 1.0f;
+#endif
}
CVector
diff --git a/src/renderer/MBlur.cpp b/src/renderer/MBlur.cpp
index 8e5fba2a..d1c9c317 100644
--- a/src/renderer/MBlur.cpp
+++ b/src/renderer/MBlur.cpp
@@ -12,6 +12,10 @@
#include "MBlur.h"
#include "postfx.h"
+#ifdef PSP2
+bool gPostFX = false;
+#endif
+
// Originally taken from RW example 'mblur'
RwRaster *CMBlur::pFrontBuffer;
@@ -216,15 +220,23 @@ CMBlur::MotionBlurRender(RwCamera *cam, uint32 red, uint32 green, uint32 blue, u
OverlayRender(cam, pFrontBuffer, color, type, bluralpha);
#else
if(BlurOn){
+ // On PSP2 the copy has to happen before the overlay: rasterRenderFast reads
+ // the display buffer, which still holds the previous frame at this point.
+#if !defined(PSP2)
if(pFrontBuffer){
if(ms_bJustInitialised)
ms_bJustInitialised = false;
else
OverlayRender(cam, pFrontBuffer, color, type, bluralpha);
}
+#endif
RwRasterPushContext(pFrontBuffer);
RwRasterRenderFast(RwCameraGetRaster(cam), 0, 0);
RwRasterPopContext();
+#ifdef PSP2
+ if(pFrontBuffer)
+ OverlayRender(cam, pFrontBuffer, color, type, bluralpha);
+#endif
}else{
OverlayRender(cam, nil, color, type, bluralpha);
}
diff --git a/src/rw/TexRead.cpp b/src/rw/TexRead.cpp
index 98e7d180..cee172fc 100644
--- a/src/rw/TexRead.cpp
+++ b/src/rw/TexRead.cpp
@@ -182,7 +182,8 @@ GetGPUcaps(GPUcaps *caps)
caps->subplatform = 0;
caps->dxtSupport = 0;
// TODO: more later
-#ifdef RW_GL3
+// librw-vita has no gl3Caps.
+#if defined(RW_GL3) && !defined(PSP2)
caps->subplatform = rw::gl3::gl3Caps.gles;
caps->dxtSupport = rw::gl3::gl3Caps.dxtSupported;
#endif
@@ -338,9 +339,46 @@ DealWithTxdWriteError(uint32 num, uint32 count, const char *text)
#define STREAMTELL(str) filesys->rwftell((str)->Type.file.fpFile)
#endif
+#ifdef PSP2
+static void
+ConvertNativeTXD(const char *filename)
+{
+ RwStream *stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, filename);
+ if(stream == nil)
+ return;
+ RwTexDictionary *txd = nil;
+ if(RwStreamFindChunk(stream, rwID_TEXDICTIONARY, nil, nil))
+ txd = RwTexDictionaryGtaStreamRead(stream);
+ RwStreamClose(stream, nil);
+ if(txd == nil)
+ return;
+
+ stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMWRITE, filename);
+ if(stream){
+ RwTexDictionaryStreamWrite(txd, stream);
+ RwStreamClose(stream, nil);
+ }
+ RwTexDictionaryDestroy(txd);
+}
+#endif
+
bool
CreateTxdImageForVideoCard()
{
+#ifdef PSP2
+ static const char *looseTxds[] = {
+ "models\\menu.txd", "models\\fonts.txd", "models\\fonts_j.txd",
+ "models\\fonts_p.txd", "models\\fonts_r.txd", "models\\frontend.txd",
+ "models\\generic.txd", "models\\hud.txd", "models\\MISC.TXD",
+ "models\\particle.txd", "models\\x360btns.txd",
+ "txd\\mainsc1.txd", "txd\\mainsc2.txd", "txd\\NEWS.TXD",
+ "txd\\SPLASH1.TXD", "txd\\SPLASH2.TXD", "txd\\SPLASH3.TXD",
+ };
+ for(uint32 i = 0; i < ARRAY_SIZE(looseTxds); i++){
+ ConvertNativeTXD(looseTxds[i]);
+ }
+#endif
+
uint8 *buf = new uint8[CDSTREAM_SECTOR_SIZE];
CDirectory *pDir = new CDirectory(TXDSTORESIZE);
CDirectory::DirectoryInfo dirInfo;
@@ -363,7 +401,8 @@ CreateTxdImageForVideoCard()
return false;
}
-#ifdef RW_GL3
+// librw-vita has no needToReadBackTextures - it always keeps texture data around.
+#if defined(RW_GL3) && !defined(PSP2)
// so we can read back DXT with GLES
// only works for textures that are not yet loaded
// so let's hope that is the case for all
@@ -406,7 +445,7 @@ CreateTxdImageForVideoCard()
delete []buf;
delete pDir;
CStreaming::RemoveTxd(i);
-#ifdef RW_GL3
+#if defined(RW_GL3) && !defined(PSP2)
rw::gl3::needToReadBackTextures = false;
#endif
return false;
@@ -441,7 +480,7 @@ CreateTxdImageForVideoCard()
RwStreamClose(img, nil);
delete []buf;
-#ifdef RW_GL3
+#if defined(RW_GL3) && !defined(PSP2)
rw::gl3::needToReadBackTextures = false;
#endif
diff --git a/src/save/SaveBuf.h b/src/save/SaveBuf.h
index aad2e1a8..fa593041 100644
--- a/src/save/SaveBuf.h
+++ b/src/save/SaveBuf.h
@@ -22,7 +22,12 @@ template
inline void
ReadSaveBuf(T* out, uint8 *&buf)
{
+ // Save buffers are not aligned, and the Vita traps unaligned wide loads.
+#if !defined(PSP2)
*out = *(T *)buf;
+#else
+ sceClibMemcpy(out, buf, sizeof(T));
+#endif
SkipSaveBuf(buf, sizeof(T));
}
@@ -31,7 +36,11 @@ inline T *
WriteSaveBuf(uint8 *&buf, const T &value)
{
T *p = (T *)buf;
+#if !defined(PSP2)
*p = value;
+#else
+ sceClibMemcpy(p, &value, sizeof(T));
+#endif
SkipSaveBuf(buf, sizeof(T));
return p;
}
diff --git a/src/skel/crossplatform.cpp b/src/skel/crossplatform.cpp
index e69c22e1..6521a220 100644
--- a/src/skel/crossplatform.cpp
+++ b/src/skel/crossplatform.cpp
@@ -1,6 +1,322 @@
#include "common.h"
#include "crossplatform.h"
+
+#ifdef PSP2
+extern SceTouchPanelInfo panelInfo[2];
+bool useRearpad = false;
+
+void *memcpy(void *dest, const void *src, size_t n)
+{
+ return sceClibMemcpy(dest, src, n);
+}
+
+static unsigned char gButtons[GLFW_GAMEPAD_BUTTON_LAST+1];
+static float gAxes[GLFW_GAMEPAD_AXIS_LAST+1];
+
+unsigned char* glfwGetJoystickButtons(int jid, int* count)
+{
+ int port_id = useRearpad ? 1 : 0;
+ SceCtrlData pad;
+ SceTouchData touch;
+ sceCtrlPeekBufferPositiveExt2(0, &pad, 1);
+ sceTouchPeek(port_id, &touch, 1);
+ gButtons[GLFW_GAMEPAD_BUTTON_CROSS] = pad.buttons & SCE_CTRL_CROSS ? GLFW_PRESS : GLFW_RELEASE;
+ gButtons[GLFW_GAMEPAD_BUTTON_CIRCLE] = pad.buttons & SCE_CTRL_CIRCLE ? GLFW_PRESS : GLFW_RELEASE;
+ gButtons[GLFW_GAMEPAD_BUTTON_SQUARE] = pad.buttons & SCE_CTRL_SQUARE ? GLFW_PRESS : GLFW_RELEASE;
+ gButtons[GLFW_GAMEPAD_BUTTON_TRIANGLE] = pad.buttons & SCE_CTRL_TRIANGLE ? GLFW_PRESS : GLFW_RELEASE;
+ gButtons[GLFW_GAMEPAD_BUTTON_LEFT_BUMPER] = pad.buttons & SCE_CTRL_L1 ? GLFW_PRESS : GLFW_RELEASE;
+ gButtons[GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER] = pad.buttons & SCE_CTRL_R1 ? GLFW_PRESS : GLFW_RELEASE;
+ gButtons[GLFW_GAMEPAD_BUTTON_BACK] = pad.buttons & SCE_CTRL_SELECT ? GLFW_PRESS : GLFW_RELEASE;
+ gButtons[GLFW_GAMEPAD_BUTTON_START] = pad.buttons & SCE_CTRL_START ? GLFW_PRESS : GLFW_RELEASE;
+ gButtons[GLFW_GAMEPAD_BUTTON_GUIDE] = GLFW_RELEASE;
+ gButtons[GLFW_GAMEPAD_BUTTON_LEFT_THUMB] = pad.buttons & SCE_CTRL_L3 ? GLFW_PRESS : GLFW_RELEASE;
+ gButtons[GLFW_GAMEPAD_BUTTON_RIGHT_THUMB] = pad.buttons & SCE_CTRL_R3 ? GLFW_PRESS : GLFW_RELEASE;
+ for (int i = 0; i < touch.reportNum; i++) {
+ if (touch.report[i].y >= (panelInfo[port_id].minAaY + panelInfo[port_id].maxAaY) / 2) {
+ if (touch.report[i].x < (panelInfo[port_id].minAaX + panelInfo[port_id].maxAaX) / 2)
+ gButtons[GLFW_GAMEPAD_BUTTON_LEFT_THUMB] = GLFW_PRESS;
+ else
+ gButtons[GLFW_GAMEPAD_BUTTON_RIGHT_THUMB] = GLFW_PRESS;
+ }
+ }
+ gButtons[GLFW_GAMEPAD_BUTTON_DPAD_UP] = pad.buttons & SCE_CTRL_UP ? GLFW_PRESS : GLFW_RELEASE;
+ gButtons[GLFW_GAMEPAD_BUTTON_DPAD_RIGHT] = pad.buttons & SCE_CTRL_RIGHT ? GLFW_PRESS : GLFW_RELEASE;
+ gButtons[GLFW_GAMEPAD_BUTTON_DPAD_DOWN] = pad.buttons & SCE_CTRL_DOWN ? GLFW_PRESS : GLFW_RELEASE;
+ gButtons[GLFW_GAMEPAD_BUTTON_DPAD_LEFT] = pad.buttons & SCE_CTRL_LEFT ? GLFW_PRESS : GLFW_RELEASE;
+ if (count)
+ *count = GLFW_GAMEPAD_BUTTON_LAST+1;
+ return gButtons;
+}
+
+float* glfwGetJoystickAxes(int jid, int* count)
+{
+ int port_id = useRearpad ? 1 : 0;
+ SceCtrlData pad;
+ SceTouchData touch;
+ sceCtrlPeekBufferPositiveExt2(0, &pad, 1);
+ sceTouchPeek(port_id, &touch, 1);
+ gAxes[GLFW_GAMEPAD_AXIS_LEFT_X] = ((float)pad.lx - 128.0f) / 128.0f;
+ gAxes[GLFW_GAMEPAD_AXIS_LEFT_Y] = ((float)pad.ly - 128.0f) / 128.0f;
+ gAxes[GLFW_GAMEPAD_AXIS_RIGHT_X] = ((float)pad.rx - 128.0f) / 128.0f;
+ gAxes[GLFW_GAMEPAD_AXIS_RIGHT_Y] = ((float)pad.ry - 128.0f) / 128.0f;
+ gAxes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER] = pad.buttons & SCE_CTRL_L2 ? 1.0f : -1.0f;
+ gAxes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER] = pad.buttons & SCE_CTRL_R2 ? 1.0f : -1.0f;
+ for (int i = 0; i < touch.reportNum; i++) {
+ if (touch.report[i].y < (panelInfo[port_id].minAaY + panelInfo[port_id].maxAaY) / 2) {
+ if (touch.report[i].x < (panelInfo[port_id].minAaX + panelInfo[port_id].maxAaX) / 2)
+ gAxes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER] = 1.0f;
+ else
+ gAxes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER] = 1.0f;
+ }
+ }
+ if (count)
+ *count = GLFW_GAMEPAD_AXIS_LAST+1;
+ return gAxes;
+}
+
+int glfwGetGamepadState(int jid, GLFWgamepadstate* state)
+{
+ unsigned char *buttons = glfwGetJoystickButtons(jid, NULL);
+ float *axes = glfwGetJoystickAxes(jid, NULL);
+ sceClibMemcpy(state->buttons, buttons, sizeof(state->buttons));
+ sceClibMemcpy(state->axes, axes, sizeof(state->axes));
+ return 1;
+}
+
+int glfwJoystickPresent(int jid)
+{
+ return jid == 0;
+}
+
+int glfwJoystickIsGamepad(int jid)
+{
+ return jid == 0;
+}
+
+int glfwGetKey(GLFWwindow* window, int key)
+{
+ return GLFW_RELEASE;
+}
+
+int glfwGetMouseButton(GLFWwindow* window, int button)
+{
+ return GLFW_RELEASE;
+}
+
+void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos)
+{
+ if (xpos)
+ *xpos = 0.0f;
+ if (ypos)
+ *ypos = 0.0f;
+ return;
+}
+
+void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos)
+{
+ return;
+}
+
+const char* glfwGetJoystickName(int jid)
+{
+ return "Vita Pad";
+}
+
+int glfwWindowShouldClose(GLFWwindow* window)
+{
+ return 0;
+}
+
+GLFWmonitor* glfwGetPrimaryMonitor(void)
+{
+ return NULL;
+}
+
+static GLFWvidmode gVideoMode { .width = 960, .height = 544 };
+const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor)
+{
+ return &gVideoMode;
+}
+
+void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
+{
+ return;
+}
+
+int clock_gettime(int clk_id, struct timespec *tp)
+{
+ if (clk_id == CLOCK_MONOTONIC) {
+ SceKernelSysClock ticks;
+ sceKernelGetProcessTime(&ticks);
+
+ tp->tv_sec = ticks / (1000 * 1000);
+ tp->tv_nsec = (ticks * 1000) % (1000 * 1000 * 1000);
+
+ return 0;
+ } else if (clk_id == CLOCK_REALTIME) {
+ time_t seconds;
+ SceDateTime time;
+ sceRtcGetCurrentClockLocalTime(&time);
+
+ sceRtcGetTime_t(&time, &seconds);
+
+ tp->tv_sec = seconds;
+ tp->tv_nsec = time.microsecond * 1000;
+
+ return 0;
+ }
+
+ return -ENOSYS;
+}
+
+int _lstat(const char *path, struct stat *buf)
+{
+ return stat(path, buf);
+}
+
+char cur_dir[PATH_MAX] = PSP2_DATA_PATH;
+char *_getcwd(char *buf, size_t size)
+{
+ if (buf != NULL)
+ strncpy(buf, cur_dir, size);
+ return cur_dir;
+}
+
+char *_realpath(const char *path, char *resolved_path)
+{
+ char data_path[PATH_MAX];
+ _getcwd(data_path, sizeof(data_path));
+
+ if (strncmp(path, "ux0:", 4) == 0)
+ strcpy(resolved_path, path);
+ else
+ sprintf(resolved_path, "%s/%s", data_path, path);
+
+ return resolved_path;
+}
+
+ssize_t readlink(const char *path, char *buf, size_t bufsiz)
+{
+ return 0;
+}
+
+int _chdir(const char *path)
+{
+ if (strncmp(path, "ux0:", 4) == 0)
+ strcpy(cur_dir, path);
+ else
+ sprintf(cur_dir, "%s/%s", cur_dir, path);
+ return 0;
+}
+
+int _mkdir(const char *pathname, mode_t mode)
+{
+ char* real = casepath(pathname);
+ int result = sceIoMkdir(real, mode) < 0 ? -1 : 0;
+ free(real);
+ return result;
+}
+
+int unlink(const char *pathname)
+{
+ char* real = casepath(pathname);
+ int result = remove(real);
+ free(real);
+ return result;
+}
+
+int _caserename(const char *old_filename, const char *new_filename)
+{
+ int result;
+ char *real_old = casepath(old_filename);
+ char *real_new = casepath(new_filename);
+ // hack so we don't even try to rename it to new_filename if it already exists
+ if (!real_new) {
+ free(real_old);
+ return -1;
+ }
+ if (!real_old)
+ result = rename(old_filename, real_new);
+ else
+ result = rename(real_old, real_new);
+ free(real_old);
+ free(real_new);
+ return result;
+}
+
+HANDLE FindFirstFile(const char* pathname, WIN32_FIND_DATA* firstfile) {
+ char pathCopy[MAX_PATH];
+ snprintf(pathCopy, sizeof(pathCopy), PSP2_DATA_PATH "/%s", pathname);
+
+ char *folder = strtok(pathCopy, "*");
+ char *extension = strtok(NULL, "*");
+
+ // because strtok doesn't return NULL for last delimiter
+ if (extension - folder == strlen(pathname))
+ extension = nil;
+
+ // Case-sensitivity and backslashes...
+ // Will be freed at the bottom
+ char *realFolder = casepath(folder);
+ if (realFolder) {
+ folder = realFolder;
+ }
+
+ strncpy(firstfile->folder, folder, sizeof(firstfile->folder));
+
+ if (extension)
+ strncpy(firstfile->extension, extension, sizeof(firstfile->extension));
+ else
+ firstfile->extension[0] = '\0';
+
+ if (realFolder)
+ free(realFolder);
+
+ HANDLE d;
+ if ((d = (HANDLE)opendir(firstfile->folder)) == NULL || !FindNextFile(d, firstfile))
+ return NULL;
+
+ return d;
+}
+
+bool FindNextFile(HANDLE d, WIN32_FIND_DATA* finddata) {
+ dirent *file;
+ static struct stat fileStats;
+ static char path[PATH_MAX], relativepath[NAME_MAX + sizeof(finddata->folder) + 1];
+ int extensionLen = strlen(finddata->extension);
+ while ((file = readdir((DIR*)d)) != NULL) {
+
+ // We only want "DT_REG"ular Files, but reportedly some FS and OSes gives DT_UNKNOWN as type.
+ if (extensionLen == 0 || strncasecmp(&file->d_name[strlen(file->d_name) - extensionLen], finddata->extension, extensionLen) == 0) {
+
+ sprintf(relativepath, "%s/%s", finddata->folder, file->d_name);
+ _realpath(relativepath, path);
+ stat(path, &fileStats);
+ strncpy(finddata->cFileName, file->d_name, sizeof(finddata->cFileName));
+ finddata->ftLastWriteTime = fileStats.st_mtime;
+ return true;
+ }
+ }
+ return false;
+}
+
+char* casepath(char const* path, bool checkPathFirst)
+{
+ char* out = (char*)malloc(PATH_MAX);
+ _realpath(path, out);
+
+ size_t l = strlen(out);
+ for (int i = l-1; i >= 0; i--) {
+ if (out[i] == '\\' || out[i] == '/' || out[i] == ' ')
+ out[i] = '\0';
+ else
+ break;
+ }
+
+ return out;
+}
+#endif
// Codes compatible with Windows and Linux
#ifndef _WIN32
@@ -25,6 +341,9 @@ void GetLocalTime_CP(SYSTEMTIME *out) {
// Compatible with Linux/POSIX and MinGW on Windows
#ifndef _WIN32
+// The Vita has its own pair above: dirent works there, but paths have to go through
+// the casepath shim, and there is no cwd to resolve them against.
+#ifndef PSP2
HANDLE FindFirstFile(const char* pathname, WIN32_FIND_DATA* firstfile) {
char pathCopy[MAX_PATH];
strcpy(pathCopy, pathname);
@@ -81,6 +400,7 @@ bool FindNextFile(HANDLE d, WIN32_FIND_DATA* finddata) {
}
return false;
}
+#endif // !PSP2
void GetDateFormat(int unused1, int unused2, SYSTEMTIME* in, int unused3, char* out, int size) {
tm linuxTime;
@@ -155,6 +475,9 @@ FILE* _fcaseopen(char const* filename, char const* mode)
return result;
}
+// _fcaseopen above is shared - it only calls casepath. These two are not: the Vita
+// supplies its own further up, resolving through sceIo and its faked cwd.
+#ifndef PSP2
int _caserename(const char *old_filename, const char *new_filename)
{
int result;
@@ -292,7 +615,8 @@ char* casepath(char const* path, bool checkPathFirst)
}
return out;
}
-#endif
+#endif // !PSP2
+#endif // !_WIN32
#ifdef __SWITCH__
/* Taken from glibc */
diff --git a/src/skel/crossplatform.h b/src/skel/crossplatform.h
index 67bb4282..9ae1a3ad 100644
--- a/src/skel/crossplatform.h
+++ b/src/skel/crossplatform.h
@@ -1,6 +1,137 @@
+#ifndef _CROSSPLATFORM_H_
+#define _CROSSPLATFORM_H_
#include
#include
+#ifdef PSP2
+
+#define GLFW_RELEASE 0
+#define GLFW_PRESS 1
+
+#define GLFW_KEY_D 68
+#define GLFW_KEY_R 82
+
+#define GLFW_MOUSE_BUTTON_1 0
+#define GLFW_MOUSE_BUTTON_2 1
+#define GLFW_MOUSE_BUTTON_3 2
+#define GLFW_MOUSE_BUTTON_4 3
+#define GLFW_MOUSE_BUTTON_5 4
+#define GLFW_MOUSE_BUTTON_6 5
+#define GLFW_MOUSE_BUTTON_7 6
+#define GLFW_MOUSE_BUTTON_8 7
+#define GLFW_MOUSE_BUTTON_LAST GLFW_MOUSE_BUTTON_8
+#define GLFW_MOUSE_BUTTON_LEFT GLFW_MOUSE_BUTTON_1
+#define GLFW_MOUSE_BUTTON_RIGHT GLFW_MOUSE_BUTTON_2
+#define GLFW_MOUSE_BUTTON_MIDDLE GLFW_MOUSE_BUTTON_3
+
+#define GLFW_JOYSTICK_1 0
+#define GLFW_JOYSTICK_2 1
+#define GLFW_JOYSTICK_3 2
+#define GLFW_JOYSTICK_4 3
+#define GLFW_JOYSTICK_5 4
+#define GLFW_JOYSTICK_6 5
+#define GLFW_JOYSTICK_7 6
+#define GLFW_JOYSTICK_8 7
+#define GLFW_JOYSTICK_9 8
+#define GLFW_JOYSTICK_10 9
+#define GLFW_JOYSTICK_11 10
+#define GLFW_JOYSTICK_12 11
+#define GLFW_JOYSTICK_13 12
+#define GLFW_JOYSTICK_14 13
+#define GLFW_JOYSTICK_15 14
+#define GLFW_JOYSTICK_16 15
+#define GLFW_JOYSTICK_LAST GLFW_JOYSTICK_16
+
+#define GLFW_GAMEPAD_BUTTON_A 0
+#define GLFW_GAMEPAD_BUTTON_B 1
+#define GLFW_GAMEPAD_BUTTON_X 2
+#define GLFW_GAMEPAD_BUTTON_Y 3
+#define GLFW_GAMEPAD_BUTTON_LEFT_BUMPER 4
+#define GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER 5
+#define GLFW_GAMEPAD_BUTTON_BACK 6
+#define GLFW_GAMEPAD_BUTTON_START 7
+#define GLFW_GAMEPAD_BUTTON_GUIDE 8
+#define GLFW_GAMEPAD_BUTTON_LEFT_THUMB 9
+#define GLFW_GAMEPAD_BUTTON_RIGHT_THUMB 10
+#define GLFW_GAMEPAD_BUTTON_DPAD_UP 11
+#define GLFW_GAMEPAD_BUTTON_DPAD_RIGHT 12
+#define GLFW_GAMEPAD_BUTTON_DPAD_DOWN 13
+#define GLFW_GAMEPAD_BUTTON_DPAD_LEFT 14
+#define GLFW_GAMEPAD_BUTTON_LAST GLFW_GAMEPAD_BUTTON_DPAD_LEFT
+
+#define GLFW_GAMEPAD_BUTTON_CROSS GLFW_GAMEPAD_BUTTON_A
+#define GLFW_GAMEPAD_BUTTON_CIRCLE GLFW_GAMEPAD_BUTTON_B
+#define GLFW_GAMEPAD_BUTTON_SQUARE GLFW_GAMEPAD_BUTTON_X
+#define GLFW_GAMEPAD_BUTTON_TRIANGLE GLFW_GAMEPAD_BUTTON_Y
+
+#define GLFW_GAMEPAD_AXIS_LEFT_X 0
+#define GLFW_GAMEPAD_AXIS_LEFT_Y 1
+#define GLFW_GAMEPAD_AXIS_RIGHT_X 2
+#define GLFW_GAMEPAD_AXIS_RIGHT_Y 3
+#define GLFW_GAMEPAD_AXIS_LEFT_TRIGGER 4
+#define GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER 5
+#define GLFW_GAMEPAD_AXIS_LAST GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER
+
+#define GLFW_CURSOR 0x00033001
+#define GLFW_CURSOR_HIDDEN 0x00034002
+#define GLFW_CURSOR_DISABLED 0x00034003
+
+typedef struct GLFWgamepadstate
+{
+ unsigned char buttons[15];
+ float axes[6];
+} GLFWgamepadstate;
+
+typedef struct GLFWvidmode
+{
+ int width;
+ int height;
+} GLFWvidmode;
+
+typedef void GLFWmonitor;
+typedef void GLFWwindow;
+
+GLFWmonitor* glfwGetPrimaryMonitor(void);
+int glfwWindowShouldClose(GLFWwindow* window);
+const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor);
+int glfwGetKey(GLFWwindow* window, int key);
+int glfwGetMouseButton(GLFWwindow* window, int button);
+void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos);
+void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos);
+int glfwJoystickPresent(int jid);
+float* glfwGetJoystickAxes(int jid, int* count);
+unsigned char* glfwGetJoystickButtons(int jid, int* count);
+int glfwJoystickIsGamepad(int jid);
+const char* glfwGetJoystickName(int jid);
+int glfwGetGamepadState(int jid, GLFWgamepadstate* state);
+void glfwSetInputMode(GLFWwindow* handle, int mode, int value);
+
+#define PATH_MAX 1024
+#define NAME_MAX 255
+
+// One place for the data directory. There is no working directory on this platform,
+// so everything relative gets resolved against this by _realpath/casepath.
+#define PSP2_DATA_PATH "ux0:data/gta3"
+
+int _lstat(const char *path, struct stat *buf);
+char *_realpath(const char *path, char *resolved_path);
+int _chdir(const char *path);
+char *_getcwd(char *buf, size_t size);
+int _mkdir(const char *pathname, mode_t mode);
+
+#elif !defined(_WIN32)
+
+// Shared code calls the underscored names so the Vita can resolve paths against
+// its data directory (it has no working directory of its own). Everywhere else
+// they are the POSIX originals. Windows is left alone - it has a real _mkdir.
+#define _lstat lstat
+#define _realpath realpath
+#define _mkdir mkdir
+#define _getcwd getcwd
+#define _chdir chdir
+
+#endif
+
// This is the common include for platform/renderer specific skeletons(glfw.cpp, win.cpp etc.) and using cross platform things (like Windows directories wrapper, platform specific global arrays etc.)
// Functions that's different on glfw and win but have same signature, should be located on platform.h.
@@ -182,3 +313,5 @@ void GetDateFormat(int, int, SYSTEMTIME*, int, char*, int);
#define GLFW_GAMEPAD_BUTTON_Y 2
#endif
+
+#endif
diff --git a/src/skel/glfw/MoviePlayer.cpp b/src/skel/glfw/MoviePlayer.cpp
index 18dd1709..8fa37530 100644
--- a/src/skel/glfw/MoviePlayer.cpp
+++ b/src/skel/glfw/MoviePlayer.cpp
@@ -1,11 +1,12 @@
// Nintendo Switch / GLFW movie player. Ported from the SDL2 build; only the
// windowing-system glue differs (GLFW proc address / buffer swap / timer).
//
-// Switch-only on purpose: Windows D3D9 builds play the intro through the win.cpp
-// skeleton (DirectShow), and the other GLFW targets never played it at all - the
-// MPEG states there have always just fallen through. Everyone else gets the stub
-// at the bottom of this file, which keeps glfw.cpp's intro state machine linkable
-// without dragging pl_mpeg and OpenAL into those builds.
+// The PS Vita gets its own backend further down, built on SceAvPlayer, behind the
+// same interface. Windows D3D9 builds play the intro through the win.cpp skeleton
+// (DirectShow), and the remaining GLFW targets never played it at all - the MPEG
+// states there have always just fallen through. Those get the stub at the bottom of
+// this file, which keeps glfw.cpp's intro state machine linkable without dragging
+// pl_mpeg and OpenAL into their builds.
#if defined __SWITCH__ && defined RW_GL3 && !defined LIBRW_SDL2 && defined AUDIO_OAL
#include "common.h"
@@ -659,6 +660,294 @@ void MoviePlayer::Draw()
}
}
+#elif defined PSP2
+
+// PS Vita backend. Same interface, different everything else: decoding is done by
+// SceAvPlayer rather than pl_mpeg, the frames arrive as GXM textures that are wrapped
+// into GL ones, and audio is pumped by a dedicated thread instead of OpenAL.
+// Lifted out of glfw.cpp, where it used to sit inline alongside the intro state machine.
+
+#include "common.h"
+#include // memalign, for the SceAvPlayer allocator callbacks
+#include
+#include
+#include "crossplatform.h"
+#include "MoviePlayer.h"
+
+#define FB_ALIGNMENT 0x40000
+#define ALIGN_MEM(x, align) (((x) + ((align) - 1)) & ~((align) - 1))
+
+SceAvPlayerHandle movie_player;
+
+GLuint movie_frame[2];
+uint8_t movie_frame_idx = 0;
+SceGxmTexture *movie_tex[2];
+GLuint movie_fs;
+GLuint movie_vs;
+GLuint movie_prog;
+
+SceUID audio_thid;
+int audio_new;
+int audio_port;
+int audio_len;
+int audio_freq;
+int audio_mode;
+
+enum {
+ PLAYER_INACTIVE,
+ PLAYER_ACTIVE,
+ PLAYER_STOP,
+};
+
+int player_state = PLAYER_INACTIVE;
+
+float movie_pos[8] = {
+ -1.0f, 1.0f,
+ -1.0f, -1.0f,
+ 1.0f, 1.0f,
+ 1.0f, -1.0f
+};
+
+float movie_texcoord[8] = {
+ 0.0f, 0.0f,
+ 0.0f, 1.0f,
+ 1.0f, 0.0f,
+ 1.0f, 1.0f
+};
+
+void *mem_alloc(void *p, uint32_t align, uint32_t size) {
+ return memalign(align, size);
+}
+
+void mem_free(void *p, void *ptr) {
+ free(ptr);
+}
+
+void *gpu_alloc(void *p, uint32_t align, uint32_t size) {
+ if (align < FB_ALIGNMENT) {
+ align = FB_ALIGNMENT;
+ }
+ size = ALIGN_MEM(size, align);
+ return vglAlloc(size, VGL_MEM_SLOW);
+}
+
+void gpu_free(void *p, void *ptr) {
+ glFinish();
+ vglFree(ptr);
+}
+
+void movie_player_audio_init(void) {
+ audio_port = -1;
+ for (int i = 0; i < 8; i++) {
+ if (sceAudioOutGetConfig(i, SCE_AUDIO_OUT_CONFIG_TYPE_LEN) >= 0) {
+ audio_port = i;
+ break;
+ }
+ }
+
+ if (audio_port == -1) {
+ audio_port = sceAudioOutOpenPort(SCE_AUDIO_OUT_PORT_TYPE_MAIN, 1024, 48000, SCE_AUDIO_OUT_MODE_STEREO);
+ audio_new = 1;
+ } else {
+ audio_len = sceAudioOutGetConfig(audio_port, SCE_AUDIO_OUT_CONFIG_TYPE_LEN);
+ audio_freq = sceAudioOutGetConfig(audio_port, SCE_AUDIO_OUT_CONFIG_TYPE_FREQ);
+ audio_mode = sceAudioOutGetConfig(audio_port, SCE_AUDIO_OUT_CONFIG_TYPE_MODE);
+ audio_new = 0;
+ }
+}
+
+void movie_player_audio_shutdown(void) {
+ if (audio_new) {
+ sceAudioOutReleasePort(audio_port);
+ } else {
+ sceAudioOutSetConfig(audio_port, audio_len, audio_freq, (SceAudioOutMode)audio_mode);
+ }
+}
+
+int movie_player_audio_thread(SceSize args, void *argp) {
+ SceAvPlayerFrameInfo frame;
+ memset(&frame, 0, sizeof(SceAvPlayerFrameInfo));
+
+ while (player_state == PLAYER_ACTIVE && sceAvPlayerIsActive(movie_player)) {
+ if (sceAvPlayerGetAudioData(movie_player, &frame)) {
+ sceAudioOutSetConfig(audio_port, 1024, frame.details.audio.sampleRate, frame.details.audio.channelCount == 1 ? SCE_AUDIO_OUT_MODE_MONO : SCE_AUDIO_OUT_MODE_STEREO);
+ sceAudioOutOutput(audio_port, frame.pData);
+ } else {
+ sceKernelDelayThread(1000);
+ }
+ }
+
+ return sceKernelExitDeleteThread(0);
+}
+
+void movie_player_draw(void) {
+ if (player_state == PLAYER_ACTIVE) {
+ if (sceAvPlayerIsActive(movie_player)) {
+ SceAvPlayerFrameInfo frame;
+ if (sceAvPlayerGetVideoData(movie_player, &frame)) {
+ movie_frame_idx = (movie_frame_idx + 1) % 2;
+ sceGxmTextureInitLinear(
+ movie_tex[movie_frame_idx],
+ frame.pData,
+ SCE_GXM_TEXTURE_FORMAT_YVU420P2_CSC1,
+ frame.details.video.width,
+ frame.details.video.height, 0);
+ sceGxmTextureSetMinFilter(movie_tex[movie_frame_idx], SCE_GXM_TEXTURE_FILTER_LINEAR);
+ sceGxmTextureSetMagFilter(movie_tex[movie_frame_idx], SCE_GXM_TEXTURE_FILTER_LINEAR);
+ glUseProgram(movie_prog);
+ glBindTexture(GL_TEXTURE_2D, movie_frame[movie_frame_idx]);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, &movie_pos[0]);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, &movie_texcoord[0]);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ vglSwapBuffers(GL_FALSE);
+ }
+ } else {
+ player_state = PLAYER_STOP;
+ }
+ }
+
+ if (player_state == PLAYER_STOP) {
+ sceAvPlayerStop(movie_player);
+ sceKernelWaitThreadEnd(audio_thid, NULL, NULL);
+ sceAvPlayerClose(movie_player);
+ movie_player_audio_shutdown();
+ player_state = PLAYER_INACTIVE;
+ glClear(GL_COLOR_BUFFER_BIT);
+ vglSwapBuffers(GL_FALSE);
+ }
+}
+
+int movie_player_inited = 0;
+void movie_player_init() {
+ if (movie_player_inited)
+ return;
+
+ sceSysmoduleLoadModule(SCE_SYSMODULE_AVPLAYER);
+
+ glGenTextures(2, movie_frame);
+ for (int i = 0; i < 2; i++) {
+ glBindTexture(GL_TEXTURE_2D, movie_frame[i]);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 960, 544, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ movie_tex[i] = vglGetGxmTexture(GL_TEXTURE_2D);
+ vglFree(vglGetTexDataPointer(GL_TEXTURE_2D));
+ }
+
+ // The precompiled GXP blobs in shaders/movie_[vf].h can no longer be fed to
+ // glShaderBinary: it now expects a vitaGL-serialized blob, so unserialize_shader
+ // reads the "GXP\0" magic as a uniform count and walks off into garbage.
+ // Compile the original Cg source (kept as a comment in those headers) instead.
+ static const char *movie_v_src =
+ "void main(\n"
+ " float2 inPos,\n"
+ " float2 inTex,\n"
+ " float4 out pos : POSITION,\n"
+ " float2 out texcoord : TEXCOORD0\n"
+ ") {\n"
+ " texcoord = inTex;\n"
+ " pos = float4(inPos, 1.f, 1.f);\n"
+ "}\n";
+ static const char *movie_f_src =
+ "float4 main(\n"
+ " float2 texcoord : TEXCOORD0,\n"
+ " uniform sampler2D tex\n"
+ ") {\n"
+ " return tex2D(tex, texcoord);\n"
+ "}\n";
+
+ movie_vs = glCreateShader(GL_CG_VERTEX_SHADER_EXT);
+ glShaderSource(movie_vs, 1, &movie_v_src, NULL);
+ glCompileShader(movie_vs);
+
+ movie_fs = glCreateShader(GL_CG_FRAGMENT_SHADER_EXT);
+ glShaderSource(movie_fs, 1, &movie_f_src, NULL);
+ glCompileShader(movie_fs);
+
+ movie_prog = glCreateProgram();
+ glAttachShader(movie_prog, movie_vs);
+ glAttachShader(movie_prog, movie_fs);
+ glBindAttribLocation(movie_prog, 0, "inPos");
+ glBindAttribLocation(movie_prog, 1, "inTex");
+ glLinkProgram(movie_prog);
+
+ movie_player_inited = 1;
+}
+
+void movie_player_play(const char *file) {
+ SceIoStat st;
+ if (sceIoGetstat(file, &st) < 0)
+ return;
+
+ movie_player_init();
+ movie_player_audio_init();
+
+ SceAvPlayerInitData playerInit;
+ sceClibMemset(&playerInit, 0, sizeof(SceAvPlayerInitData));
+
+ playerInit.memoryReplacement.allocate = mem_alloc;
+ playerInit.memoryReplacement.deallocate = mem_free;
+ playerInit.memoryReplacement.allocateTexture = gpu_alloc;
+ playerInit.memoryReplacement.deallocateTexture = gpu_free;
+
+ playerInit.basePriority = 0xA0;
+ playerInit.numOutputVideoFrameBuffers = 2;
+ playerInit.autoStart = GL_TRUE;
+
+ movie_player = sceAvPlayerInit(&playerInit);
+
+ sceAvPlayerAddSource(movie_player, file);
+
+ audio_thid = sceKernelCreateThread("movie_audio_thread", movie_player_audio_thread, 0x10000100 - 10, 0x4000, 0, 0, NULL);
+ sceKernelStartThread(audio_thid, 0, NULL);
+
+ player_state = PLAYER_ACTIVE;
+}
+
+namespace MoviePlayer
+{
+ void Play(const char *path)
+ {
+ // The shared intro code asks for pl_mpeg's .mpg by a path relative to the
+ // data directory; SceAvPlayer wants .mp4 and an absolute one.
+ char rel[256];
+ strncpy(rel, path, sizeof(rel) - 1);
+ rel[sizeof(rel) - 1] = '\0';
+ char *ext = strrchr(rel, '.');
+ if (ext != nil)
+ strcpy(ext, ".mp4");
+
+ char full[PATH_MAX];
+ _realpath(rel, full);
+
+ // This build has always shipped one set of intro videos, so the localised
+ // title reel the shared code asks for on French and German is not there.
+ // Fall back to the plain one rather than silently dropping the intro.
+ SceIoStat st;
+ if (sceIoGetstat(full, &st) < 0) {
+ char *ger = strstr(rel, "GER.mp4");
+ if (ger != nil) {
+ strcpy(ger, ".mp4");
+ _realpath(rel, full);
+ }
+ }
+
+ movie_player_play(full);
+ }
+
+ void Draw() { movie_player_draw(); }
+
+ bool IsActive() { return player_state != PLAYER_INACTIVE; }
+
+ void Stop()
+ {
+ if (player_state == PLAYER_ACTIVE)
+ player_state = PLAYER_STOP; // movie_player_draw does the teardown
+ }
+}
+
#else // !(RW_GL3 && !LIBRW_SDL2)
#include "MoviePlayer.h"
diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp
index 5a9faaf8..6f5073dc 100644
--- a/src/skel/glfw/glfw.cpp
+++ b/src/skel/glfw/glfw.cpp
@@ -14,7 +14,9 @@ DWORD _dwOperatingSystemVersion;
long _dwOperatingSystemVersion;
#ifndef __SWITCH__
#ifndef __APPLE__
+#if !defined(PSP2)
#include
+#endif
#else
#include
#include
@@ -68,6 +70,18 @@ long _dwOperatingSystemVersion;
#define MAX_SUBSYSTEMS (16)
+#ifdef PSP2
+#include
+#include
+#include
+#include "../message.h"
+
+int _newlib_heap_size_user = 256 * 1024 * 1024;
+
+#define DATA_PATH PSP2_DATA_PATH
+#define RT_INI_PATH DATA_PATH "/" "10thae.ini"
+#endif
+
rw::EngineOpenParams openParams;
static RwBool ForegroundApp = TRUE;
@@ -122,11 +136,13 @@ void _psCreateFolder(const char *path)
#else
struct stat info;
char fullpath[PATH_MAX];
- realpath(path, fullpath);
+ // Underscored names: the Vita resolves these against its data directory, every
+ // other platform aliases them to the POSIX originals in crossplatform.h.
+ _realpath(path, fullpath);
- if (lstat(fullpath, &info) != 0) {
+ if (_lstat(fullpath, &info) != 0) {
if (errno == ENOENT || (errno != EACCES && !S_ISDIR(info.st_mode))) {
- mkdir(fullpath, 0755);
+ _mkdir(fullpath, 0755);
}
}
#endif
@@ -560,6 +576,8 @@ psInitialize(void)
#elif defined (__SWITCH__)
svcGetInfo(&_dwMemAvailPhys, InfoType_UsedMemorySize, CUR_PROCESS_HANDLE, 0);
debug("Physical memory size %llu\n", _dwMemAvailPhys);
+#elif defined(PSP2)
+ _dwMemAvailPhys = 140 * 1024 * 1024;
#else
struct sysinfo systemInfo;
sysinfo(&systemInfo);
@@ -929,6 +947,9 @@ psSelectDevice()
return TRUE;
}
+// None of these exist on the Vita: there is no window and no real GLFW, only the
+// input stubs in crossplatform.cpp.
+#if !defined(PSP2)
#ifndef GET_KEYBOARD_INPUT_FROM_X11
void keypressCB(GLFWwindow* window, int key, int scancode, int action, int mods);
#endif
@@ -939,6 +960,7 @@ void cursorEnterCB(GLFWwindow* window, int entered);
void windowFocusCB(GLFWwindow* window, int focused);
void windowIconifyCB(GLFWwindow* window, int iconified);
void joysChangeCB(int jid, int event);
+#endif
bool IsThisJoystickBlacklisted(int i)
{
@@ -963,6 +985,8 @@ void _InputInitialiseJoys()
PSGLOBAL(joy1id) = -1;
PSGLOBAL(joy2id) = -1;
+ // The Vita reads its pad through SceCtrl, so there is nothing to remap.
+#if !defined(PSP2)
// Load our gamepad mappings.
#define SDL_GAMEPAD_DB_PATH "gamecontrollerdb.txt"
FILE *f = fopen(SDL_GAMEPAD_DB_PATH, "rb");
@@ -992,6 +1016,7 @@ void _InputInitialiseJoys()
if (EnvControlConfig != nil) {
glfwUpdateGamepadMappings(EnvControlConfig);
}
+#endif // !PSP2
for (int i = 0; i <= GLFW_JOYSTICK_LAST; i++) {
if (glfwJoystickPresent(i) && !IsThisJoystickBlacklisted(i)) {
@@ -1004,6 +1029,9 @@ void _InputInitialiseJoys()
}
}
+#ifdef PSP2
+ ControlsManager.InitDefaultControlConfigJoyPad(16);
+#else
if (PSGLOBAL(joy1id) != -1) {
int count;
glfwGetJoystickButtons(PSGLOBAL(joy1id), &count);
@@ -1012,11 +1040,14 @@ void _InputInitialiseJoys()
#endif
ControlsManager.InitDefaultControlConfigJoyPad(count);
}
+#endif
}
long _InputInitialiseMouse()
{
+#if !defined(PSP2)
glfwSetInputMode(PSGLOBAL(window), GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
+#endif
return 0;
}
@@ -1025,6 +1056,8 @@ void psPostRWinit(void)
RwVideoMode vm;
RwEngineGetVideoModeInfo(&vm, GcurSelVM);
+ // There is no window to hang callbacks on under PSP2, and the resolution is fixed.
+#if !defined(PSP2)
glfwSetFramebufferSizeCallback(PSGLOBAL(window), resizeCB);
#ifndef IGNORE_MOUSE_KEYBOARD
#ifndef GET_KEYBOARD_INPUT_FROM_X11
@@ -1037,12 +1070,15 @@ void psPostRWinit(void)
glfwSetWindowIconifyCallback(PSGLOBAL(window), windowIconifyCB);
glfwSetWindowFocusCallback(PSGLOBAL(window), windowFocusCB);
glfwSetJoystickCallback(joysChangeCB);
+#endif
_InputInitialiseJoys();
_InputInitialiseMouse();
+#if !defined(PSP2)
if(!(vm.flags & rwVIDEOMODEEXCLUSIVE))
glfwSetWindowSize(PSGLOBAL(window), RsGlobal.maximumWidth, RsGlobal.maximumHeight);
+#endif
// Make sure all keys are released
CPad::GetPad(0)->Clear(true);
@@ -1341,7 +1377,7 @@ void HandleExit()
#endif
}
-#ifndef _WIN32
+#if !defined(_WIN32) && !defined(PSP2)
void terminateHandler(int sig, siginfo_t *info, void *ucontext) {
RsGlobal.quit = TRUE;
}
@@ -1353,6 +1389,9 @@ void dummyHandler(int sig){
#endif
#endif
+// Everything from here to the end of checkKeyPresses is GLFW window and keyboard
+// plumbing that the Vita has no counterpart for.
+#if !defined(PSP2)
void resizeCB(GLFWwindow* window, int width, int height) {
/*
* Handle event to ensure window contents are displayed during re-size
@@ -1797,14 +1836,18 @@ void checkKeyPresses()
}
#endif
+#endif // !PSP2
// R* calls that in ControllerConfig, idk why
void
_InputTranslateShiftKeyUpDown(RsKeyCodes *rs) {
+#if !defined(PSP2)
RsKeyboardEventHandler(lshiftStatus ? rsKEYDOWN : rsKEYUP, &(*rs = rsLSHIFT));
RsKeyboardEventHandler(rshiftStatus ? rsKEYDOWN : rsKEYUP, &(*rs = rsRSHIFT));
+#endif
}
+#if !defined(PSP2)
// TODO this only works in frontend(and luckily only frontend use this). Fun fact: if I get pos manually in game, glfw reports that it's > 32000
void
cursorCB(GLFWwindow* window, double xpos, double ypos) {
@@ -1821,7 +1864,9 @@ void
cursorEnterCB(GLFWwindow* window, int entered) {
PSGLOBAL(cursorIsInWindow) = !!entered;
}
+#endif // !PSP2
+#if !defined(PSP2)
void
windowFocusCB(GLFWwindow* window, int focused) {
WindowFocused = !!focused;
@@ -1831,6 +1876,7 @@ void
windowIconifyCB(GLFWwindow* window, int iconified) {
WindowIconified = !!iconified;
}
+#endif // !PSP2
/*
*****************************************************************************
@@ -1869,9 +1915,35 @@ WinMain(HINSTANCE instance,
#endif
#else
+#ifdef PSP2
+SceTouchPanelInfo panelInfo[2];
+#endif
int
main(int argc, char *argv[])
{
+#ifdef PSP2
+ sceCtrlSetSamplingModeExt(SCE_CTRL_MODE_ANALOG_WIDE);
+ sceTouchSetSamplingState(SCE_TOUCH_PORT_FRONT, SCE_TOUCH_SAMPLING_STATE_START);
+ sceTouchSetSamplingState(SCE_TOUCH_PORT_BACK, SCE_TOUCH_SAMPLING_STATE_START);
+ // The stock clocks are not enough for this; the port has always overclocked.
+ scePowerSetArmClockFrequency(444);
+ scePowerSetBusClockFrequency(222);
+ scePowerSetGpuClockFrequency(222);
+ scePowerSetGpuXbarClockFrequency(166);
+ sceTouchGetPanelInfo(SCE_TOUCH_PORT_FRONT, &panelInfo[0]);
+ sceTouchGetPanelInfo(SCE_TOUCH_PORT_BACK, &panelInfo[1]);
+
+ // Marker file: bail out with a readable message instead of crashing later if the
+ // game data was never copied over.
+ if (!fileExists(RT_INI_PATH))
+ {
+ showMsgDialog("Liberty's gone quiet.\nNo game files found at " DATA_PATH);
+ sceKernelExitProcess(1);
+ }
+#ifndef ENABLE_STDOUT
+ fclose(stdout);
+#endif
+#endif
#endif
RwV2d pos;
RwInt32 i;
@@ -1880,7 +1952,7 @@ main(int argc, char *argv[])
InitMemoryMgr();
#endif
-#if !defined(_WIN32) && !defined(__SWITCH__)
+#if !defined(_WIN32) && !defined(__SWITCH__) && !defined(PSP2)
struct sigaction act;
act.sa_sigaction = terminateHandler;
act.sa_flags = SA_SIGINFO;
@@ -1933,7 +2005,9 @@ main(int argc, char *argv[])
openParams.width = RsGlobal.maximumWidth;
openParams.height = RsGlobal.maximumHeight;
openParams.windowtitle = RsGlobal.appName;
+#if !defined(PSP2)
openParams.window = &PSGLOBAL(window);
+#endif
ControlsManager.MakeControllerActionsBlank();
ControlsManager.InitDefaultControlConfiguration();
@@ -2053,7 +2127,9 @@ main(int argc, char *argv[])
}
#endif
+#if !defined(PSP2)
initkeymap();
+#endif
while ( TRUE )
{
@@ -2096,7 +2172,9 @@ main(int argc, char *argv[])
while( !RsGlobal.quit && !FrontEndMenuManager.m_bWantToRestart && !glfwWindowShouldClose(PSGLOBAL(window)))
#endif
{
+#if !defined(PSP2)
glfwPollEvents();
+#endif
#ifdef GET_KEYBOARD_INPUT_FROM_X11
checkKeyPresses();
#endif
@@ -2174,7 +2252,7 @@ main(int argc, char *argv[])
case GS_INIT_ONCE:
{
//CoUninitialize();
-
+
#ifdef PS2_MENU
extern char version_name[64];
if ( CGame::frenchGame || CGame::germanGame )
@@ -2188,7 +2266,7 @@ main(int argc, char *argv[])
#endif
if ( !CGame::InitialiseOnceAfterRW() )
RsGlobal.quit = TRUE;
-
+
#ifdef PS2_MENU
gGameState = GS_INIT_PLAYING_GAME;
#else
@@ -2202,7 +2280,7 @@ main(int argc, char *argv[])
case GS_INIT_FRONTEND:
{
LoadingScreen(nil, nil, "loadsc0");
-
+
FrontEndMenuManager.m_bGameNotLoaded = true;
CMenuManager::m_bStartUpFrontEndRequested = true;
@@ -2534,6 +2612,7 @@ void CapturePad(RwInt32 padID)
return;
}
+#if !defined(PSP2)
void joysChangeCB(int jid, int event)
{
if (event == GLFW_CONNECTED && !IsThisJoystickBlacklisted(jid)) {
@@ -2558,6 +2637,7 @@ void joysChangeCB(int jid, int event)
PSGLOBAL(joy2id) = -1;
}
}
+#endif // !PSP2
#if (defined(_MSC_VER))
int strcasecmp(const char* str1, const char* str2)
diff --git a/src/skel/message.cpp b/src/skel/message.cpp
new file mode 100644
index 00000000..84c4095b
--- /dev/null
+++ b/src/skel/message.cpp
@@ -0,0 +1,126 @@
+// Vita-only (SceGxm message dialog). Guarded so the file is an empty translation
+// unit when the premake/CMake source glob pulls it into non-PSP2 platform builds;
+// the Vita target itself builds via its own Makefile.
+#ifdef PSP2
+
+#include
+
+#include
+#include
+#include
+#include
+
+#include "message.h"
+
+GxmDrawer::GxmDrawer() {
+ constexpr SceGxmInitializeParams params{
+ 0, DISPLAY_MAX_PENDING_SWAPS, gxmVsyncCb, sizeof(void *),
+ SCE_GXM_DEFAULT_PARAMETER_BUFFER_SIZE};
+ sceGxmInitialize(¶ms);
+
+
+ for (auto &buf: displayBuf_) {
+ buf.data = dramAlloc(4 * DISPLAY_STRIDE_IN_PIXELS * DISPLAY_HEIGHT,
+ &buf.uid);
+ sceGxmColorSurfaceInit(&buf.surf, SCE_GXM_COLOR_FORMAT_A8B8G8R8,
+ SCE_GXM_COLOR_SURFACE_LINEAR,
+ SCE_GXM_COLOR_SURFACE_SCALE_NONE,
+ SCE_GXM_OUTPUT_REGISTER_SIZE_32BIT,
+ DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_STRIDE_IN_PIXELS, buf.data);
+ sceGxmSyncObjectCreate(&buf.sync);
+ }
+}
+
+GxmDrawer::~GxmDrawer() {
+ sceGxmTerminate();
+
+ for (const auto &buf: displayBuf_) {
+ sceGxmSyncObjectDestroy(buf.sync);
+ sceKernelFreeMemBlock(buf.uid);
+ }
+}
+
+void GxmDrawer::gxmSwap() {
+ sceGxmPadHeartbeat(&displayBuf_[backBufferIndex_].surf,
+ displayBuf_[backBufferIndex_].sync);
+ sceGxmDisplayQueueAddEntry(displayBuf_[frontBufferIndex_].sync,
+ displayBuf_[backBufferIndex_].sync,
+ &displayBuf_[backBufferIndex_].data);
+ frontBufferIndex_ = backBufferIndex_;
+ backBufferIndex_ = (backBufferIndex_ + 1) % DISPLAY_BUFFER_COUNT;
+}
+
+void GxmDrawer::clearScreenBuffer() const {
+ sceClibMemset(displayBuf_[backBufferIndex_].data,
+ static_cast(0xFF000000),
+ DISPLAY_HEIGHT * DISPLAY_STRIDE_IN_PIXELS * 4);
+}
+
+void GxmDrawer::updateSceDialog() const {
+ const SceCommonDialogUpdateParam updateParam{
+ {nullptr, displayBuf_[backBufferIndex_].data,
+ static_cast(0),
+ static_cast(0), DISPLAY_WIDTH,
+ DISPLAY_HEIGHT, DISPLAY_STRIDE_IN_PIXELS},
+ displayBuf_[backBufferIndex_].sync};
+ sceCommonDialogUpdate(&updateParam);
+}
+
+void *GxmDrawer::dramAlloc(const std::uint32_t size, SceUID *uid) {
+ void *mem;
+ *uid = sceKernelAllocMemBlock("gpu_mem",
+ SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW,
+ ALIGN(size, 256 * 1024), nullptr);
+ sceKernelGetMemBlockBase(*uid, &mem);
+ sceGxmMapMemory(mem, ALIGN(size, 256 * 1024),
+ static_cast(
+ SCE_GXM_MEMORY_ATTRIB_READ |
+ SCE_GXM_MEMORY_ATTRIB_WRITE));
+ return mem;
+}
+
+void GxmDrawer::gxmVsyncCb(const void *callbackData) {
+ const SceDisplayFrameBuf fb{
+ sizeof(SceDisplayFrameBuf),
+ *static_cast(const_cast(callbackData)),
+ DISPLAY_STRIDE_IN_PIXELS,
+ 0,
+ DISPLAY_WIDTH,
+ DISPLAY_HEIGHT};
+ sceDisplaySetFrameBuf(&fb, SCE_DISPLAY_SETBUF_NEXTFRAME);
+}
+
+void showMsgDialog(const char *message) {
+ GxmDrawer gxmDrawer;
+
+ SceMsgDialogUserMessageParam msgParam;
+ sceClibMemset(&msgParam, 0, sizeof(SceMsgDialogUserMessageParam));
+ msgParam.buttonType = SCE_MSG_DIALOG_BUTTON_TYPE_OK;
+ msgParam.msg = reinterpret_cast(message);
+
+ SceMsgDialogParam param;
+ sceMsgDialogParamInit(¶m);
+ param.mode = SCE_MSG_DIALOG_MODE_USER_MSG;
+ param.userMsgParam = &msgParam;
+
+ constexpr SceCommonDialogConfigParam confParam{};
+ sceCommonDialogSetConfigParam(&confParam);
+ sceMsgDialogInit(¶m);
+
+ while (sceMsgDialogGetStatus() != SCE_COMMON_DIALOG_STATUS_FINISHED) {
+ gxmDrawer.clearScreenBuffer();
+ gxmDrawer.updateSceDialog();
+
+ gxmDrawer.gxmSwap();
+ sceDisplayWaitVblankStart();
+ }
+ sceMsgDialogTerm();
+}
+
+int fileExists(const char *path) {
+ SceIoStat stat;
+ return sceIoGetstat(path, &stat) >= 0;
+}
+
+#endif // PSP2
diff --git a/src/skel/message.h b/src/skel/message.h
new file mode 100644
index 00000000..7fb06622
--- /dev/null
+++ b/src/skel/message.h
@@ -0,0 +1,53 @@
+#ifndef MESSAGE_H
+#define MESSAGE_H
+
+// Vita-only (SceGxm). Everything here pulls psp2/* headers, so keep it out of
+// non-PSP2 builds that include this header via the source glob. See message.cpp.
+#ifdef PSP2
+
+#include
+#include
+
+#include
+#include
+
+#ifndef ALIGN
+#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
+#endif // ALIGN
+
+struct DisplayBuffer {
+ void *data;
+ SceGxmSyncObject *sync;
+ SceGxmColorSurface surf;
+ SceUID uid;
+}; // struct DisplayBuffer
+
+class GxmDrawer {
+public:
+ GxmDrawer();
+ ~GxmDrawer();
+
+ void gxmSwap();
+ void clearScreenBuffer() const;
+ void updateSceDialog() const;
+
+ static void *dramAlloc(std::uint32_t size, SceUID *uid);
+ static void gxmVsyncCb(const void *callbackData);
+
+private:
+ static constexpr int DISPLAY_WIDTH{960};
+ static constexpr int DISPLAY_HEIGHT{544};
+ static constexpr int DISPLAY_STRIDE_IN_PIXELS{1024};
+ static constexpr int DISPLAY_BUFFER_COUNT{2};
+ static constexpr int DISPLAY_MAX_PENDING_SWAPS{1};
+
+ std::uint32_t backBufferIndex_{}, frontBufferIndex_{};
+ std::array displayBuf_{};
+}; // class GxmDrawer
+
+void showMsgDialog(const char *message);
+int fileExists(const char *path);
+
+#endif // PSP2
+
+#endif //MESSAGE_H