Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .github/workflows/vita.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Build PlayStation Vita VPK

on:
push:
branches:
- master
paths:
- '.github/workflows/vita.yml'
- 'CMakeLists.txt'
- 'assets/vita/**'
- 'src/**'
- 'thirdparty/**'
- 'tools/vita/**'
pull_request:
paths:
- '.github/workflows/vita.yml'
- 'CMakeLists.txt'
- 'assets/vita/**'
- 'src/**'
- 'thirdparty/**'
- 'tools/vita/**'
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest
container:
image: vitasdk/vitasdk:latest

steps:
- name: Check out the repository
uses: actions/checkout@v6

- name: Install build requirements
run: apk add --no-cache cmake ninja

- name: Build the release VPK
run: sh tools/vita/build-vpk.sh

- name: Verify the VPK
run: test -s build/vita/release/src/SF_EXE/OpenShadowFlare.vpk

- name: Upload the VPK
uses: actions/upload-artifact@v4
with:
name: openshadowflare-vita-release
path: build/vita/release/src/SF_EXE/OpenShadowFlare.vpk
if-no-files-found: error
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ project(
LANGUAGES C CXX
)

if(NINTENDO_SWITCH)
if(NINTENDO_SWITCH OR VITA OR CMAKE_SYSTEM_NAME STREQUAL "Vita")
set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
endif()

Expand Down
Binary file added assets/vita/sce_sys/icon0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions documentation/vita-port.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# PlayStation Vita

Build with [VitaSDK](https://vitasdk.org/) on Linux or WSL:

```sh
sh tools/vita/build-vpk.sh
```

Output: `build/vita/release/src/SF_EXE/OpenShadowFlare.vpk`. GitHub Actions
uploads the same file as `openshadowflare-vita-release`.

The VPK does not include retail data. Copy game installation to:

```text
ux0:data/OpenShadowFlare/ShadowFlare/
```

It must include `SFlare.Cfg` and `System/Title/Pattern/Title.njp`.

## Controls

| Vita control | Game input |
| --- | --- |
| Front touchscreen | Pointer and primary click |
| Left stick | Virtual pointer |
| Cross / Start | Confirm; Cross also primary click |
| Circle / Select | Back; Circle also secondary click |
| D-pad | Menu navigation |
| Triangle / Square | Inventory / map |
| L / R | Mission list / run toggle |

The port uses double-buffered software scaling at 960x544 and the system IME
for character names.
24 changes: 24 additions & 0 deletions shadowflare/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -257,3 +257,27 @@ if(NINTENDO_SWITCH)
endif()
nx_create_nro(osf ICON "${PROJECT_SOURCE_DIR}/readme/sf-logo-sm.jpg")
endif()

if(VITA OR CMAKE_SYSTEM_NAME STREQUAL "Vita")
target_compile_definitions(
shadowflare_core PRIVATE
SF_RETAIL_ROOT_FALLBACK="ux0:data/OpenShadowFlare/ShadowFlare")

if(NOT DEFINED ENV{VITASDK})
message(FATAL_ERROR
"VITASDK is not set. Configure the Vita build with "
"-DCMAKE_TOOLCHAIN_FILE=$VITASDK/share/vita.toolchain.cmake.")
endif()
include("$ENV{VITASDK}/share/vita.cmake")
vita_create_self(OpenShadowFlare.self osf)
vita_create_vpk(
OpenShadowFlare.vpk
OSFLR0001
OpenShadowFlare.self
VERSION "01.00"
NAME "OpenShadowFlare"
FILE
"${PROJECT_SOURCE_DIR}/assets/vita/sce_sys/icon0.png"
"sce_sys/icon0.png"
)
endif()
8 changes: 4 additions & 4 deletions src/SF_EXE/resources/item_inventory_resource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ bool ItemInventoryResource::prepareGroups(
std::snprintf(
retail_path,
sizeof(retail_path),
"System\\Game\\Pattern\\Item%04zu.njp",
index);
"System\\Game\\Pattern\\Item%04lu.njp",
static_cast<unsigned long>(index));
std::string resource_error;
if (!groups_[index].load(
resolveRetailPath(data_root_, retail_path),
Expand Down Expand Up @@ -128,8 +128,8 @@ bool ItemInventoryResource::preparePatterns(
std::snprintf(
retail_path,
sizeof(retail_path),
"System\\Game\\Pattern\\Item%04zu.njp",
index);
"System\\Game\\Pattern\\Item%04lu.njp",
static_cast<unsigned long>(index));
std::string resource_error;
if (!groups_[index].loadSelectedPatterns(
resolveRetailPath(
Expand Down
33 changes: 28 additions & 5 deletions src/SF_EXE/runtime/game_runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
#include "runtime/gameplay_artwork.hpp"
#include "runtime/gameplay_ui_controller.hpp"
#include "runtime/input_adapter.hpp"
#ifdef OSF_PLATFORM_VITA
#include "runtime/platform/platform_text_input.hpp"
#endif
#include "runtime/presentation/surface_presenter.hpp"
#include "runtime/runtime_renderer.hpp"
#include "runtime/state_bindings.hpp"
Expand Down Expand Up @@ -328,13 +331,30 @@ class Runtime final : public osf::runtime::FrameApplication {
running = false;
}
break;
}
case osf::GameState::character_select: {
input_.characterSelect().saved_game_count =
resources_.savedGameCount();
characterFrame_ =
}
case osf::GameState::character_select: {
input_.characterSelect().saved_game_count =
resources_.savedGameCount();
#ifdef OSF_PLATFORM_VITA
if (!pendingCharacterName_.empty()) {
input_.characterSelect().text_input =
pendingCharacterName_;
pendingCharacterName_.clear();
}
#endif
characterFrame_ =
characterSelectState_.update(
input_.characterSelect());
#ifdef OSF_PLATFORM_VITA
if (characterFrame_.mode_action ==
osf::CharacterSelectModeAction::begin_name_entry) {
pendingCharacterName_ =
osf::runtime::openPlatformTextInput(
"Enter character name",
characterSelectState_.data().character_name,
15);
}
#endif
audio_.playCharacterSelectFrame(characterFrame_);
if (characterFrame_.action ==
osf::CharacterSelectAction::return_to_title) {
Expand Down Expand Up @@ -552,6 +572,9 @@ class Runtime final : public osf::runtime::FrameApplication {
osf::debug::FrameProfiler profiler_;
#endif
osf::GameConfig gameConfig_;
#ifdef OSF_PLATFORM_VITA
std::string pendingCharacterName_;
#endif
osf::PlayerLoadRequest gameplayPlayer_;
std::filesystem::path dataRoot_;
osf::ResourceManager resources_;
Expand Down
8 changes: 8 additions & 0 deletions src/SF_EXE/runtime/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ std::filesystem::path findDataRoot() {
return std::filesystem::path{};
};

#ifdef OSF_PLATFORM_VITA
const std::filesystem::path vitaDataRoot =
"ux0:data/OpenShadowFlare/ShadowFlare";
if (isDataRoot(vitaDataRoot)) {
return vitaDataRoot;
}
#endif

std::error_code error;
const std::filesystem::path fromWorkingDirectory =
searchParents(std::filesystem::current_path(error));
Expand Down
14 changes: 14 additions & 0 deletions src/SF_EXE/runtime/platform/platform_text_input.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <string>

namespace osf::runtime {

// Opens a platform text-input method for one short field. An empty result
// means that the user cancelled or did not enter any text.
std::string openPlatformTextInput(
const std::string& title,
const std::string& initialText,
int maxLength);

} // namespace osf::runtime
3 changes: 3 additions & 0 deletions thirdparty/tal/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_link_libraries(tal PRIVATE ALSA::ALSA)
elseif(NINTENDO_SWITCH)
target_sources(tal PRIVATE tal_switch.c)
elseif(VITA OR CMAKE_SYSTEM_NAME STREQUAL "Vita")
target_sources(tal PRIVATE tal_vita.c)
target_link_libraries(tal PUBLIC SceAudio_stub SceKernelThreadMgr_stub)
else()
target_sources(tal PRIVATE tal_null.c)
endif()
Loading
Loading