Not actually related to C programming on the Vita!
Note from Wololo: This tutorial on How to program homebrews for your PS Vita was initially published by /Talk member thevitamaster, as part of our monthly tutorial contest. Thevitamaster won the August tutorial contest for the PS Vita (a $10 Amazon gift code) with this entry. You can find the original post here.
Hi guys,
welcome to part one of my new tutorial series on programming for the Vita, where we will be laying the groundwork for you to develop your own homebrew / port games to the vita! Today we will be doing a graphical hello world with the unofficial VitaSDK and vita2dlib.
Requirements:
– Vita with Henkaku
********************************************************************************************************************
– Unofficial VitaSDK and vita2dlib installed on a linux system (
see my last tutorial)
********************************************************************************************************************
– Local Area Network connectivity between Vita and linux system
********************************************************************************************************************
So, don’t be scared but here comes a dump of code. Scroll a little bit down for the explanation.
Main.c
#include <psp2/display.h>
#include <psp2/moduleinfo.h>
#include <psp2/kernel/processmgr.h>
#include
PSP2_MODULE_INFO(0, 0, "HelloWorld");
int main() {
vita2d_pgf *pgf;
vita2d_init();
vita2d_set_clear_color(RGBA8(0, 0, 0, 255));
pgf = vita2d_load_default_pgf();
while(1){
vita2d_start_drawing();
vita2d_clear_screen();
vita2d_pgf_draw_text(pgf, 700, 30, RGBA8(0, 255, 0, 255), 1.0f, "Hello, World!");
vita2d_end_drawing();
vita2d_swap_buffers();
}
vita2d_fini();
vita2d_free_pgf(pgf);
sceKernelExitProcess(0);
return 0;
}
Explanation of main.c (above):
First of all, let’s look at the include directives (for example the “#include <psp2/display.h>” directive).
These include directives, also called preprocessor directives, because they are enumerated into files on a “preparation” pass, basically just tell the C compiler to include (e.g. copy) the contents of this header file (display.h) into the file being compiled (our file main.c in this case). With our Vita, also called an embedded linux device, we use these include directives to use functions specific to some aspect of the hardware / software. For example, if we do “#include <psp2/ctrl.h>”, we want access to controller-related functions, variables, enums (a new variable type documented in the header and defined in source), and so forth, so we can for example do a bitwise AND (“&” in C) with our own subset variable of type SceCtrlData called “pad.buttons” and the controller state of the UP directional button, which is called “SCE_CTRL_UP”.
********************************************************************************************************************
Now, before we dive into the skeletal function calls of a Vita program, we have to define how a function routine is created / defined in C and also, how to then call it. So, let’s assume we have a system capable of running assembled C code in a stable manner as well as capable of printing printf() calls to the standard output, which is presumptuously a terminal. Here’s how the creation and calling of a function in the same file would work:
example.c:
#include
// Creation of function routine
int printdec(int decimal){
printf("%d", decimal);
return 0;
}
int main(){
// Function call
printdec(123);
return 0;
}
So, now we can see that function creation and function call always follow a pattern. The pattern for function creation is:
[variable type you want to return] [function name]([function arguments]){
[routine]
}
On the other hand, the pattern for function calling is:
[function name]([function arguments]);
Note the semicolon at the end of every function call, variable assignment, defintion, … as this tells the C compiler that the instruction ends there.
********************************************************************************************************************
Now we can look at the first skeletal function call in Vita program, namely
PSP2_MODULE_INFO(0, 0, "HelloWorld");
This function is defined in psp2/moduleinfo.h and, as you can see, it gives the Vita system some basic information on our program, namely
- 1. The attribute (No idea what that is)
- 2. The version of your program
- 3. Your program name. This has a maximum length of 27 characters.
We pass this information in said order in the function arguments of PSP2_MODULE_INFO.
********************************************************************************************************************
Next up:
int main(){
In the C language, as opposed to say Python 2.7, every function call that should be executed has to be in the routine of the main() function. So, basically, every function call we want to make, we put within the “{” curly braces following “int main()”.
********************************************************************************************************************
Following, we have:
vita2d_pgf *pgf;
This variabble defintion basically creates a new pointer variable of type vita2d_pgf which we can assign to the start of a pgf file with the appropriate function call.
********************************************************************************************************************
vita2d_init();
With that function call, we initialize the vita2d graphics engine.
********************************************************************************************************************
vita2d_set_clear_color(RGBA8(0, 0, 0, 255));
Woah, that’s quite something to dissect, but we’ll get it done.
So, first of all, we have the function call vita2d_set_clear_color() which sets the color the vita should display when the screen is cleared of all to be rendered elements.
Inside that, as the colour argument, we have the RGBA8() macro, which is defined (we’ll get to that in part 2) in vita2dlib. Basically, this macro allows you to specify colours.
Here, we can specify the Red, Green, Blue and Alpha values, the max of every single value being 255, and the min 0.
The Red value specifies the amount of red, the Green value the amount of green and so on.
But what does the alpha value specify??
Well, that value specifies the opacity of the object, so how little transparency there is: 255 is no transparency and 0 is all transparent.
********************************************************************************************************************
pgf = vita2d_load_default_pgf();
This is a variable assignment. Here, we assign the return value of vita2d_load_default_pgf() to the pointer pgf of type vita2d_pgf.
********************************************************************************************************************
while(1){
Here, we have an infinite while loop. We do this to draw the values all the time and not draw them once and then exit.
********************************************************************************************************************
vita2d_start_drawing();
Here, we tell the vita to start the rendering process in memory not allocated to the display.
[!Important]But, we won’t see anything on the vita screen until we call vita2d_swap_buffers();[/!Important]
Because then, our instructions will be swapped to the vita memory allocated to the display and the display controller will then execute the instructions.
********************************************************************************************************************
vita2d_clear_screen();
We set the vita screen to display all black.
Remember vita2d_set_clear_color() and RGBA8()?
********************************************************************************************************************
vita2d_pgf_draw_text(pgf, 700, 30, RGBA8(0, 255, 0, 255), 1.0f, "Hello World!");
So, this function call let’s us draw text on the screen of the vita!
The arguments to the function are in following order:
- 1. Assigned pointer of type vita2d_pgf
- 2. X coordinate of start of text
- 3. Y coordinate of start of text
- 4. Colour value created with the RGBA8 macro
- 5. Value for the size of text of type float (e.g with decimal points)
- 6. String to display
To 3. and 4.: Both of these values must be of type int (e.g. no decimal points).
********************************************************************************************************************
vita2d_end_drawing();
End the creation of the graphical elements in memory; the memory can now be swapped.
********************************************************************************************************************
vita2d_swap_buffers();
Swap the new drawn memory with the old one.
********************************************************************************************************************
}
End the infinte while loop.
********************************************************************************************************************
vita2d_fini();
Start all the cleanup processes, etc. of the vita2d engine.
********************************************************************************************************************
vita2d_free_pgf(pgf);
Free the memory allocated to the pgf font.
********************************************************************************************************************
sceKernelExitProcess(0);
Exit the program using a function created in the unofficial VitaSDK.
********************************************************************************************************************
return 0;
Because our main function is of type int, we return the value 0.
This is standard programming practice in C.
Makefile:
TITLE_ID = VITA2DTST
TARGET = vita2dsample
OBJS = main.o
LIBS = -lvita2d -lSceKernel_stub -lSceDisplay_stub -lSceGxm_stub \
-lSceSysmodule_stub -lSceCtrl_stub -lScePgf_stub \
-lSceCommonDialog_stub -lfreetype -lpng -ljpeg -lz -lm -lc
PREFIX = arm-vita-eabi
CC = $(PREFIX)-gcc
CFLAGS = -Wl,-q -Wall -O3
ASFLAGS = $(CFLAGS)
all: $(TARGET).vpk
%.vpk: eboot.bin
vita-mksfoex -s TITLE_ID=$(TITLE_ID) "$(TARGET)" param.sfo
eboot.bin: $(TARGET).velf
vita-make-fself $< $@
%.velf: %.elf
vita-elf-create $< $@
$(TARGET).elf: $(OBJS)
$(CC) $(CFLAGS) $^ $(LIBS) -o $@
%.o: %.png
$(PREFIX)-ld -r -b binary -o $@ $^
clean:
@rm -rf $(TARGET).vpk $(TARGET).velf $(TARGET).elf $(OBJS) \
eboot.bin param.sfo
This “just works”.
Compilation of our Hello World program:
1. Copy the code of main.c and Makefile and put them in two seperate files with their equivalent names.
2. Open a terminal, change your directory to that of the two files and run:
make
Installation:
1. Open molecularshell on your vita and press select
2. Open a terminal and type:
cd ~
wget https://github.com/xyzz/Vita_Doom/releases/download/1.0/vitadoom.vpk
ftp [ip displayed on your vita] [port displayed on your vita] (without the brackets)
put ~/vitadoom.vpk /ux0:vitadoom.vpk
3. Press circle on your vita and navigate to ux0:, then press cross when the vitadoom.vpk file is highlighted
4. Press select again
5. Open a terminal and type:
ftp [ip displayed on your vita] [port displayed on your vita] (without the brackets)
put path/to/eboot.bin /ux0:app/DOOM00000/eboot.bin
put path/to/param.sfo /ux0:app/DOOM00000/sce_sys/param.sfo
6. Close molecularshell and open the Doom bubble
7. ???
8. Profit!!!