Vita porting
This commit is contained in:
13
BUILDING.md
13
BUILDING.md
@@ -95,3 +95,16 @@ Dependencies and requirements:
|
||||
```sh
|
||||
make -j$(nproc) # To build using all cores
|
||||
```
|
||||
|
||||
# Playstation Vita
|
||||
|
||||
Dependencies and requirements:
|
||||
|
||||
* [VitaSDK](https://vitasdk.org/)
|
||||
|
||||
1. Install VitaSDK and SDL2 (included automatically in the standard installation of VitaSDK)
|
||||
2. CD to `vita` folder by typing `cd src/platform/vita` in the terminal on the `sm` root folder.
|
||||
3. Create a build folder and enter it `mkdir -p build && cd build`
|
||||
4. type `cmake ..` to create the makefile for Vita adapted to your development configuration.
|
||||
5. type `make` to compile the Vita version.
|
||||
6. Transfer the `sm.vpk` to your Vita and install it with VitaShell or your favorite installer.
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
# sm-vita
|
||||
|
||||
This is a (work in progress) porting of [sm](https://github.com/snesrev/sm) project on the Vita console.
|
||||
|
||||
- For building, check [BUILDING.md](BUILDING.md#playstation-vita)
|
||||
- For more details on how to run it, check the Vita-specific [README](src/platform/vita/README.md)
|
||||
|
||||
# sm
|
||||
|
||||
Our discord server is: https://discord.gg/AJJbJAzNNJ
|
||||
@@ -7,4 +14,3 @@ Early version. It has bugs and the code is messy.
|
||||
For building instructions, see: https://github.com/snesrev/sm/blob/main/BUILDING.md
|
||||
|
||||
Put sm.smc (sha1 hash da957f0d63d14cb441d215462904c4fa8519c613) in the root folder. When running, it will run both versions and compare frame by frame. If it detects a mismatch, it saves a snapshot in saves/ and displays a counter on screen counting down from 300.
|
||||
|
||||
|
||||
21
src/main.c
21
src/main.c
@@ -25,6 +25,9 @@
|
||||
#ifdef __SWITCH__
|
||||
#include "switch_impl.h"
|
||||
#endif
|
||||
#ifdef __VITA__
|
||||
#include "platform/vita/src/vita_impl.h"
|
||||
#endif
|
||||
|
||||
static void playAudio(Snes *snes, SDL_AudioDeviceID device, int16_t *audioBuffer);
|
||||
static void renderScreen(Snes *snes, SDL_Renderer *renderer, SDL_Texture *texture);
|
||||
@@ -163,6 +166,8 @@ static SDL_HitTestResult HitTestCallback(SDL_Window *win, const SDL_Point *pt, v
|
||||
}
|
||||
|
||||
void RtlDrawPpuFrame(uint8 *pixel_buffer, size_t pitch, uint32 render_flags) {
|
||||
if(pixel_buffer == NULL)
|
||||
return;
|
||||
uint8 *ppu_pixels = g_other_image ? g_my_pixels : g_pixels;
|
||||
for (size_t y = 0; y < 240; y++)
|
||||
memcpy((uint8_t *)pixel_buffer + y * pitch, ppu_pixels + y * 256 * 4, 256 * 4);
|
||||
@@ -282,6 +287,10 @@ static void SdlRenderer_Destroy(void) {
|
||||
SDL_DestroyRenderer(g_renderer);
|
||||
}
|
||||
|
||||
static void SdlRenderer_BeforeFrame(void** ptr) {
|
||||
|
||||
}
|
||||
|
||||
static void SdlRenderer_BeginDraw(int width, int height, uint8 **pixels, int *pitch) {
|
||||
g_sdl_renderer_rect.w = width;
|
||||
g_sdl_renderer_rect.h = height;
|
||||
@@ -305,6 +314,7 @@ static void SdlRenderer_EndDraw(void) {
|
||||
static const struct RendererFuncs kSdlRendererFuncs = {
|
||||
&SdlRenderer_Init,
|
||||
&SdlRenderer_Destroy,
|
||||
&SdlRenderer_BeforeFrame,
|
||||
&SdlRenderer_BeginDraw,
|
||||
&SdlRenderer_EndDraw,
|
||||
};
|
||||
@@ -315,6 +325,9 @@ static const struct RendererFuncs kSdlRendererFuncs = {
|
||||
int main(int argc, char** argv) {
|
||||
#ifdef __SWITCH__
|
||||
SwitchImpl_Init();
|
||||
#endif
|
||||
#ifdef __VITA__
|
||||
VitaImpl_Init();
|
||||
#endif
|
||||
argc--, argv++;
|
||||
const char *config_file = NULL;
|
||||
@@ -367,12 +380,16 @@ int main(int argc, char** argv) {
|
||||
int window_width = custom_size ? g_config.window_width : g_current_window_scale * g_snes_width;
|
||||
int window_height = custom_size ? g_config.window_height : g_current_window_scale * g_snes_height;
|
||||
|
||||
#ifdef __VITA__
|
||||
VitaRenderer_Create(&g_renderer_funcs);
|
||||
#else
|
||||
if (g_config.output_method == kOutputMethod_OpenGL) {
|
||||
g_win_flags |= SDL_WINDOW_OPENGL;
|
||||
OpenGLRenderer_Create(&g_renderer_funcs);
|
||||
} else {
|
||||
g_renderer_funcs = kSdlRendererFuncs;
|
||||
}
|
||||
#endif
|
||||
|
||||
// init snes, load rom
|
||||
const char* filename = argv[0] ? argv[0] : "sm.smc";
|
||||
@@ -507,8 +524,9 @@ int main(int argc, char** argv) {
|
||||
g_gamepad_buttons = 0;
|
||||
inputs |= g_gamepad_buttons;
|
||||
|
||||
uint8 is_replay = RtlRunFrame(inputs);
|
||||
g_renderer_funcs.BeforeFrame((void**)&snes->ppu->renderBuffer);
|
||||
|
||||
uint8 is_replay = RtlRunFrame(inputs);
|
||||
frameCtr++;
|
||||
g_snes->disableRender = (g_turbo ^ (is_replay & g_replay_turbo)) && (frameCtr & (g_turbo ? 0xf : 0x7f)) != 0;
|
||||
|
||||
@@ -526,7 +544,6 @@ int main(int argc, char** argv) {
|
||||
SDL_SetWindowTitle(g_window, kWindowTitle);
|
||||
}
|
||||
}
|
||||
|
||||
// if vsync isn't working, delay manually
|
||||
curTick = SDL_GetTicks();
|
||||
|
||||
|
||||
@@ -144,6 +144,9 @@ static bool OpenGLRenderer_Init(SDL_Window *window) {
|
||||
static void OpenGLRenderer_Destroy(void) {
|
||||
}
|
||||
|
||||
static void OpenGLRenderer_BeforeFrame(void** ptr) {
|
||||
}
|
||||
|
||||
static void OpenGLRenderer_BeginDraw(int width, int height, uint8 **pixels, int *pitch) {
|
||||
int size = width * height;
|
||||
|
||||
@@ -207,6 +210,7 @@ static void OpenGLRenderer_EndDraw(void) {
|
||||
static const struct RendererFuncs kOpenGLRendererFuncs = {
|
||||
&OpenGLRenderer_Init,
|
||||
&OpenGLRenderer_Destroy,
|
||||
&OpenGLRenderer_BeforeFrame,
|
||||
&OpenGLRenderer_BeginDraw,
|
||||
&OpenGLRenderer_EndDraw,
|
||||
};
|
||||
|
||||
1
src/platform/vita/.gitignore
vendored
Normal file
1
src/platform/vita/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
build/
|
||||
101
src/platform/vita/CMakeLists.txt
Normal file
101
src/platform/vita/CMakeLists.txt
Normal file
@@ -0,0 +1,101 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
if(NOT DEFINED CMAKE_TOOLCHAIN_FILE)
|
||||
if(DEFINED ENV{VITASDK})
|
||||
set(CMAKE_TOOLCHAIN_FILE "$ENV{VITASDK}/share/vita.toolchain.cmake" CACHE PATH "toolchain file")
|
||||
else()
|
||||
message(FATAL_ERROR "Please define VITASDK to point to your SDK path!")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
project(sm)
|
||||
include("${VITASDK}/share/vita.cmake" REQUIRED)
|
||||
|
||||
find_package(SDL2 REQUIRED PATHS ${VITASDK}/arm-vita-eabi/lib/cmake/SDL2)
|
||||
|
||||
set(VITA_APP_NAME "Super Metroid")
|
||||
set(VITA_TITLEID "SPMETROID")
|
||||
set(VITA_VERSION "00.10")
|
||||
set(VITA_VPKNAME "sm")
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,-q,--wrap,fopen,--wrap,mkdir -Ofast -fomit-frame-pointer -ffast-math \
|
||||
-mfloat-abi=hard -ffast-math -fsingle-precision-constant -fno-tree-vectorize -mtune=cortex-a9 -mfpu=neon -DNDEBUG -D__VITA__")
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
../../main.c
|
||||
../../sm_80.c
|
||||
../../sm_81.c
|
||||
../../sm_82.c
|
||||
../../sm_84.c
|
||||
../../sm_85.c
|
||||
../../sm_86.c
|
||||
../../sm_87.c
|
||||
../../sm_88.c
|
||||
../../sm_89.c
|
||||
../../sm_8b.c
|
||||
../../sm_8d.c
|
||||
../../sm_8f.c
|
||||
../../sm_90.c
|
||||
../../sm_91.c
|
||||
../../sm_92.c
|
||||
../../sm_93.c
|
||||
../../sm_94.c
|
||||
../../sm_9b.c
|
||||
../../sm_a0.c
|
||||
../../sm_a2.c
|
||||
../../sm_a3.c
|
||||
../../sm_a4.c
|
||||
../../sm_a5.c
|
||||
../../sm_a6.c
|
||||
../../sm_a7.c
|
||||
../../sm_a8.c
|
||||
../../sm_a9.c
|
||||
../../sm_aa.c
|
||||
../../sm_ad.c
|
||||
../../sm_b2.c
|
||||
../../sm_b3.c
|
||||
../../sm_b4.c
|
||||
../../sm_cpu_infra.c
|
||||
../../sm_rtl.c
|
||||
../../snes/apu.c
|
||||
../../snes/cart.c
|
||||
../../snes/cpu.c
|
||||
../../snes/dma.c
|
||||
../../snes/dsp.c
|
||||
../../snes/input.c
|
||||
../../snes/ppu.c
|
||||
../../snes/snes.c
|
||||
../../snes/snes_other.c
|
||||
../../snes/spc.c
|
||||
../../spc_player.c
|
||||
../../util.c
|
||||
../../config.c
|
||||
../../tracing.c
|
||||
src/vita_impl.c
|
||||
)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
SDL2::SDL2
|
||||
)
|
||||
|
||||
vita_create_self(${PROJECT_NAME}.self ${PROJECT_NAME})
|
||||
vita_create_vpk(${PROJECT_NAME}.vpk ${VITA_TITLEID} ${PROJECT_NAME}.self
|
||||
VERSION ${VITA_VERSION}
|
||||
NAME ${VITA_APP_NAME}
|
||||
FILE sm.ini sm.ini
|
||||
FILE sce_sys/icon0.png sce_sys/icon0.png
|
||||
FILE sce_sys/livearea/contents/bg.png sce_sys/livearea/contents/bg.png
|
||||
FILE sce_sys/livearea/contents/startup.png sce_sys/livearea/contents/startup.png
|
||||
FILE sce_sys/livearea/contents/template.xml sce_sys/livearea/contents/template.xml
|
||||
)
|
||||
|
||||
set(PSVITAIP "192.168.178.32" CACHE STRING "PSVita IP (for FTP access)")
|
||||
|
||||
add_custom_target(send
|
||||
COMMAND echo Sending to ${PSVITAIP}...
|
||||
COMMAND echo destroy | nc ${PSVITAIP} 1338
|
||||
COMMAND cp sm.self eboot.bin
|
||||
COMMAND curl -T ftp://${PSVITAIP}:1337/ux0:/app/${VITA_TITLEID}/
|
||||
COMMAND echo launch ${VITA_TITLEID} | nc ${PSVITAIP} 1338
|
||||
DEPENDS ${VITA_VPKNAME}.vpk-vpk
|
||||
)
|
||||
4
src/platform/vita/README.md
Normal file
4
src/platform/vita/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Super Metroid Vita porting
|
||||
|
||||
To run, you need sm.smc (sha1 hash da957f0d63d14cb441d215462904c4fa8519c613). Create a directory in <code>ux0:data/smetroid</code> and put the file in it.
|
||||
Finally, install sm.vpk using VitaShell and the app bubble will appear in your LiveArea.
|
||||
BIN
src/platform/vita/sce_sys/icon0.png
Normal file
BIN
src/platform/vita/sce_sys/icon0.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 97 B |
BIN
src/platform/vita/sce_sys/livearea/contents/bg.png
Normal file
BIN
src/platform/vita/sce_sys/livearea/contents/bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 167 B |
BIN
src/platform/vita/sce_sys/livearea/contents/startup.png
Normal file
BIN
src/platform/vita/sce_sys/livearea/contents/startup.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 121 B |
11
src/platform/vita/sce_sys/livearea/contents/template.xml
Normal file
11
src/platform/vita/sce_sys/livearea/contents/template.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<livearea style="a1" format-ver="01.00" content-rev="1">
|
||||
<livearea-background>
|
||||
<image>bg.png</image>
|
||||
</livearea-background>
|
||||
|
||||
<gate>
|
||||
<startup-image>startup.png</startup-image>
|
||||
</gate>
|
||||
</livearea>
|
||||
44
src/platform/vita/sm.ini
Normal file
44
src/platform/vita/sm.ini
Normal file
@@ -0,0 +1,44 @@
|
||||
[General]
|
||||
# Automatically save state on quit and reload on start
|
||||
Autosave = 0
|
||||
|
||||
# Disable the SDL_Delay that happens each frame (Gives slightly better perf if your
|
||||
# display is set to exactly 60hz)
|
||||
DisableFrameDelay = 1
|
||||
|
||||
[Graphics]
|
||||
# Window size ( Auto or WidthxHeight )
|
||||
WindowSize = 960x544
|
||||
|
||||
# Window scale (1=100%, 2=200%, 3=300%, etc.)
|
||||
WindowScale = 1
|
||||
|
||||
# Use an optimized (but potentially more buggy) SNES PPU implementation
|
||||
NewRenderer = 1
|
||||
|
||||
# Display the world map with higher resolution
|
||||
EnhancedMode7 = 0
|
||||
|
||||
# Don't keep the aspect ratio
|
||||
IgnoreAspectRatio = 1
|
||||
|
||||
# Enable this option to remove the sprite limits per scan line
|
||||
NoSpriteLimits = 0
|
||||
|
||||
[Sound]
|
||||
EnableAudio = 1
|
||||
|
||||
# DSP frequency in samples per second (e.g. 48000, 44100, 32000, 22050, 11025)
|
||||
AudioFreq = 48000
|
||||
|
||||
# number of separate sound channels (1=mono, 2=stereo)
|
||||
AudioChannels = 2
|
||||
|
||||
# Audio buffer size in samples (power of 2; e.g., 4096, 2048, 1024) [try 1024 if sound is crackly]. The higher the more lag before you hear sounds.
|
||||
AudioSamples = 512
|
||||
|
||||
[GamepadMap]
|
||||
# Any keys used in KeyMap can be used also in this section.
|
||||
# The shoulder button is called L1/Lb and L2, and the thumbstick button is called L3
|
||||
Controls = DpadUp, DpadDown, DpadLeft, DpadRight, Back, Start, B, A, Y, X, Lb, Rb
|
||||
|
||||
114
src/platform/vita/src/vita_impl.c
Normal file
114
src/platform/vita/src/vita_impl.c
Normal file
@@ -0,0 +1,114 @@
|
||||
#include <vitasdk.h>
|
||||
#include <stdio.h>
|
||||
#include <SDL.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdbool.h>
|
||||
#include "../../../types.h"
|
||||
#include "../../../util.h"
|
||||
|
||||
int __real_mkdir(const char *fname, mode_t mode);
|
||||
FILE * __real_fopen(char *fname, char *mode);
|
||||
|
||||
int _newlib_heap_size_user = 128 * 1024 * 1024;
|
||||
|
||||
void VitaImpl_Init() {
|
||||
scePowerSetArmClockFrequency(444);
|
||||
scePowerSetBusClockFrequency(222);
|
||||
scePowerSetGpuClockFrequency(222);
|
||||
scePowerSetGpuXbarClockFrequency(166);
|
||||
//To create base directory if necessary
|
||||
mkdir("", 0777);
|
||||
}
|
||||
|
||||
void OpenGLRenderer_Create(struct RendererFuncs *funcs) {
|
||||
|
||||
}
|
||||
|
||||
// State Vita SDL Renderer
|
||||
static SDL_Renderer *vita_renderer;
|
||||
static SDL_Texture *vita_texture;
|
||||
static SDL_Rect window_rect, rendering_rect, texture_rect;
|
||||
|
||||
static bool VitaRenderer_Init(SDL_Window *window) {
|
||||
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||
if (renderer == NULL) {
|
||||
printf("Failed to create renderer: %s\n", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
vita_renderer = renderer;
|
||||
|
||||
vita_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 256, 240);
|
||||
if (vita_texture == NULL) {
|
||||
printf("Failed to create texture: %s\n", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
window_rect.x = 169;
|
||||
window_rect.y = 0;
|
||||
window_rect.w = 622;
|
||||
window_rect.h = 544;
|
||||
|
||||
rendering_rect.x = 0;
|
||||
rendering_rect.y = 0;
|
||||
rendering_rect.w = 256;
|
||||
rendering_rect.h = 240;
|
||||
|
||||
texture_rect.x = 0;
|
||||
texture_rect.y = 0;
|
||||
texture_rect.w = 256;
|
||||
texture_rect.h = 224;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void VitaRenderer_Destroy(void) {
|
||||
SDL_DestroyTexture(vita_texture);
|
||||
SDL_DestroyRenderer(vita_renderer);
|
||||
}
|
||||
|
||||
static void VitaRenderer_BeforeFrame(void **memoryBuffer) {
|
||||
int pitch;
|
||||
if (SDL_LockTexture(vita_texture, &rendering_rect, memoryBuffer, &pitch) != 0) {
|
||||
printf("Failed to lock texture: %s\n", SDL_GetError());
|
||||
//TODO fail program
|
||||
}
|
||||
}
|
||||
|
||||
static void VitaRenderer_BeginDraw(int width, int height, uint8 **pixels, int *pitch) {
|
||||
*pixels = NULL;
|
||||
}
|
||||
|
||||
static void VitaRenderer_EndDraw(void) {
|
||||
SDL_UnlockTexture(vita_texture);
|
||||
SDL_RenderClear(vita_renderer);
|
||||
SDL_RenderCopy(vita_renderer, vita_texture, &texture_rect, &window_rect);
|
||||
SDL_RenderPresent(vita_renderer);
|
||||
}
|
||||
|
||||
static const struct RendererFuncs kVitaRendererFuncs = {
|
||||
&VitaRenderer_Init,
|
||||
&VitaRenderer_Destroy,
|
||||
&VitaRenderer_BeforeFrame,
|
||||
&VitaRenderer_BeginDraw,
|
||||
&VitaRenderer_EndDraw,
|
||||
};
|
||||
|
||||
void VitaRenderer_Create(struct RendererFuncs *funcs) {
|
||||
*funcs = kVitaRendererFuncs;
|
||||
}
|
||||
|
||||
int __wrap_mkdir(const char *fname, mode_t mode) {
|
||||
char patched_fname[256];
|
||||
sprintf(patched_fname, "ux0:data/smetroid/%s", fname);
|
||||
return __real_mkdir(patched_fname, mode);
|
||||
}
|
||||
|
||||
FILE *__wrap_fopen(char *fname, char *mode) {
|
||||
//Use the vpk-provided ini
|
||||
if(strcmp(fname, "sm.ini") == 0) {
|
||||
return __real_fopen(fname, mode);
|
||||
}
|
||||
char patched_fname[256];
|
||||
sprintf(patched_fname, "ux0:data/smetroid/%s", fname);
|
||||
return __real_fopen(patched_fname, mode);
|
||||
}
|
||||
5
src/platform/vita/src/vita_impl.h
Normal file
5
src/platform/vita/src/vita_impl.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
void VitaImpl_Init();
|
||||
void VitaRenderer_Create(struct RendererFuncs *funcs);
|
||||
bool VitaRenderer_BeforeFrame(void **memoryBuffer);
|
||||
@@ -14,7 +14,11 @@
|
||||
void RtlRunFrameCompare(uint16 input, int run_what);
|
||||
|
||||
enum RunMode { RM_BOTH, RM_MINE, RM_THEIRS };
|
||||
#ifdef __VITA__
|
||||
uint8 g_runmode = RM_MINE;
|
||||
#else
|
||||
uint8 g_runmode = RM_BOTH;
|
||||
#endif
|
||||
|
||||
extern int g_got_mismatch_count;
|
||||
|
||||
|
||||
@@ -245,8 +245,8 @@ void ppu_handleVblank(Ppu* ppu) {
|
||||
}
|
||||
|
||||
static inline void ClearBackdrop(PpuPixelPrioBufs *buf) {
|
||||
for (size_t i = 0; i != arraysize(buf->data); i += 4)
|
||||
*(uint64*)&buf->data[i] = 0x0500050005000500;
|
||||
for (size_t i = 0; i != arraysize(buf->data); i += 1)
|
||||
buf->data[i] = 0x0000;
|
||||
}
|
||||
|
||||
void ppu_runLine(Ppu* ppu, int line) {
|
||||
|
||||
@@ -8,6 +8,7 @@ typedef struct SDL_Window SDL_Window;
|
||||
struct RendererFuncs {
|
||||
bool (*Initialize)(SDL_Window *window);
|
||||
void (*Destroy)();
|
||||
void (*BeforeFrame)(void** buffer);
|
||||
void (*BeginDraw)(int width, int height, uint8 **pixels, int *pitch);
|
||||
void (*EndDraw)();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user